package com.wzdigit.wms.wms.service.transfer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wzdigit.framework.Shift;
import com.wzdigit.framework.utils.RedisUtil;
import com.wzdigit.mes.basic.api.BasicSimpleCodeRuleApi;
import com.wzdigit.wms.basic.client.PagerInfo;
import com.wzdigit.wms.basic.client.request.transfer.GdnCtnrCommitRequest;
import com.wzdigit.wms.basic.client.request.transfer.GdnCtnrRequest;
import com.wzdigit.wms.basic.client.request.transfer.GdnDtlGoodsSimpleReq;
import com.wzdigit.wms.basic.client.request.transfer.SnAndQtyRequest;
import com.wzdigit.wms.basic.client.response.Result;
import com.wzdigit.wms.basic.client.response.basic.LocationResponse;
import com.wzdigit.wms.basic.client.response.transfer.*;
import com.wzdigit.wms.basic.domain.basic.Sku;
import com.wzdigit.wms.basic.domain.basic.Warehouse;
import com.wzdigit.wms.basic.domain.dtos.inventory.UpdateInventoryInputDto;
import com.wzdigit.wms.basic.domain.dtos.transfer.GdnDtlDto;
import com.wzdigit.wms.basic.domain.dtos.transfer.GdnOmsInfoDto;
import com.wzdigit.wms.basic.domain.dtos.trsn.TrsnDtlDto;
import com.wzdigit.wms.basic.domain.dtos.trsn.TrsnDto;
import com.wzdigit.wms.basic.domain.inventory.Goods;
import com.wzdigit.wms.basic.domain.inventory.GoodsHis;
import com.wzdigit.wms.basic.domain.inventory.GoodsLog;
import com.wzdigit.wms.basic.domain.inventory.InvTrans;
import com.wzdigit.wms.basic.domain.mapping.CodeMapping;
import com.wzdigit.wms.basic.domain.mid.IfPostback;
import com.wzdigit.wms.basic.domain.order.TrsnDtl;
import com.wzdigit.wms.basic.domain.order.TrsnDtlGoods;
import com.wzdigit.wms.basic.domain.order.TrsnHeader;
import com.wzdigit.wms.basic.domain.transfer.*;
import com.wzdigit.wms.basic.enums.*;
import com.wzdigit.wms.basic.enums.log.GoodsLogEnum;
import com.wzdigit.wms.basic.enums.writeback.BackResultEnum;
import com.wzdigit.wms.basic.enums.writeback.OtherSystemEnum;
import com.wzdigit.wms.basic.enums.writeback.TopicEnum;
import com.wzdigit.wms.basic.mapper.core.basic.SkuMapper;
import com.wzdigit.wms.basic.mapper.core.basic.WarehouseMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsHisMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsLogMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsMapper;
import com.wzdigit.wms.basic.mapper.core.mapping.CodeMappingMapper;
import com.wzdigit.wms.basic.mapper.core.mid.IfPostbackMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.*;
import com.wzdigit.wms.basic.service.code.InventoryService;
import com.wzdigit.wms.common.entity.StatusCode;
import com.wzdigit.wms.basic.util.HttpSessionUtil;
import com.wzdigit.wms.wms.client.web.service.common.wms2sap.ZDT_Reserve;
import com.wzdigit.wms.wms.client.web.service.wms2oms.sync.ctnr.Req_Pl;
import com.wzdigit.wms.wms.client.web.service.wms2sap.delivery.ZDT_003_ReqRecordsHead;
import com.wzdigit.wms.wms.client.web.service.wms2sap.delivery.ZDT_003_ReqRecordsHeadItems;
import com.wzdigit.wms.wms.client.web.service.wms2sap.delivery.ZDT_003_ReqRecordsHeadItemsRSV_A;
import com.wzdigit.wms.wms.client.web.service.wms2sap.delivery.ZDT_003_ReqRecordsHeadItemsRSV_B;
import com.wzdigit.wms.wms.domain.OmsShipBusiness;
import com.wzdigit.wms.wms.domain.dto.TmsDispatchReturnDtl;
import com.wzdigit.wms.wms.domain.dto.TmsDispatchReturnHeader;
import com.wzdigit.wms.wms.domain.dto.TmsResponse;
import com.wzdigit.wms.wms.domain.dto.goods.TmsTakeOverDtl;
import com.wzdigit.wms.wms.domain.dto.goods.TmsTakeOverDto;
import com.wzdigit.wms.wms.dto.inventory.GoodsDto;
import com.wzdigit.wms.wms.exception.SystemException;
import com.wzdigit.wms.wms.http.TmsHttpClient;
import com.wzdigit.wms.wms.mapper.OmsShipBusinessMapper;
import com.wzdigit.wms.wms.mapper.TmsTakeOverMapper;
import com.wzdigit.wms.wms.service.basic.LocationService;
import com.wzdigit.wms.wms.service.core.GoodsService;
import com.wzdigit.wms.wms.service.core.transfer.DocPreAlocService;
import com.wzdigit.wms.wms.service.mapping.CodeMappingNewService;
import com.wzdigit.wms.wms.service.order.TrsnService;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.utils.BeanUtil;
import net.dreamlu.mica.core.utils.CollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @ClassName GdnCtnrService
 * @Description 发货装运PDA
 * @Author LHR-PC
 * @Date 2021/3/4 14:13
 * @Version
 **/
@Service
@Slf4j
public class GdnCtnrService {

    @Autowired
    DocPreAlocService docPreAlocService;

    @Autowired
    private IfPostbackMapper ifPostbackMapper;

    @Autowired
    private CtnrLoadingGoodsMapper ctnrLoadingGoodsMapper;

    @Autowired
    private GdnRemainHeaderMapper gdnRemainHeaderMapper;

    @Autowired
    private GdnRemainDtlMapper gdnRemainDtlMapper;

    @Autowired
    private PicktaskDtlMapper picktaskDtlMapper;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private GdnDtlGoodsMapper gdnDtlGoodsMapper;

    @Autowired
    private CtnrLoadingMapper ctnrLoadingMapper;

    @Autowired
    private GdnHeaderMapper gdnHeaderMapper;


    @Autowired
    private GdnDtlMapper gdnDtlMapper;


    @Autowired
    private TrsnService trsnService;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private PicktaskDtlGoodsMapper picktaskDtlGoodsMapper;

    @Autowired
    private WarehouseMapper warehouseMapper;

    @Autowired
    private GdnRemainDtlGoodsMapper gdnRemainDtlGoodsMapper;

    @Autowired
    private GoodsHisMapper goodsHisMapper;

    @Autowired
    private CodeMappingNewService codeMappingNewService;

    @Autowired
    private LocationService locationService;

    @Autowired
    GoodsService goodsService;

    @Autowired
    GoodsLogMapper goodsLogMapper;

    @Autowired
    private TmsHttpClient tmsHttpClient;

    @Autowired
    private OmsShipBusinessMapper omsShipBusinessMapper;

    @Autowired
    private CodeMappingMapper codeMappingMapper;

    @Reference(version = "${dubbo.consumers.basic.simple.code.rule.api:1.0.0}", check = false)
    BasicSimpleCodeRuleApi basicSimpleCodeRuleApi;

    private static final int WAITLOAD = GdnDtlStatusEnum.WAITLOAD.code; //待装柜

    private static final int LOADING = GdnDtlStatusEnum.LOADING.code; //装柜中

    private static final java.lang.String NOTE_NO = "TRSN_ORDER";

    private static final String DOC_NO = "DOCNO";

    @Value(value = "${tms.web.service.header}")
    private String header;

    @Value(value = "${tms.web.service.dispatchReturn}")
    private String dispatchReturn;

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private TmsTakeOverMapper tmsTakeOverMapper;

    /**
     * 发货装运任务页面
     *
     * @param gdnCtnrRequest
     * @return
     */
    public PagerInfo<List<GdnCtnrTaskResponse>> list(GdnCtnrRequest gdnCtnrRequest) {
        if (gdnCtnrRequest == null) {
            gdnCtnrRequest = new GdnCtnrRequest();
        }
        if (gdnCtnrRequest.getMaxResultCount() == 0) {
            gdnCtnrRequest.setMaxResultCount(10);
        }
        PagerInfo<List<GdnCtnrTaskResponse>> pagerInfo = new PagerInfo<List<GdnCtnrTaskResponse>>();

        if (gdnCtnrRequest.getShipDateLong() != null) {
            gdnCtnrRequest.setShipDate(new Date(gdnCtnrRequest.getShipDateLong()));
        }

        //加个过滤条件
        gdnCtnrRequest.setGdnType(GdnTypeEnum.D01.getCode());

        //未经处理的
        List<GdnCtnrTaskResponse> list = gdnHeaderMapper.selectGndCtnrList(gdnCtnrRequest); //单条精确到柜
        if (CollectionUtil.isEmpty(list)) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR);
        }
        //实际返回的
        List<GdnCtnrTaskResponse> result = new ArrayList<>();
        if (!CollectionUtil.isEmpty(list)) {
            list.stream().forEach(new Consumer<GdnCtnrTaskResponse>() {
                @Override
                public void accept(GdnCtnrTaskResponse gndCtnrResponse) {
                    if (GdnDtlStatusEnum.WAITLOAD.getCode() == gndCtnrResponse.getStatus()) {
                        gndCtnrResponse.setStatusDesc(GdnDtlStatusEnum.getEnumByCode(WAITLOAD).enumName);
                    }
                    if (GdnDtlStatusEnum.LOADING.getCode() == gndCtnrResponse.getStatus()) {
                        gndCtnrResponse.setStatusDesc(GdnDtlStatusEnum.getEnumByCode(LOADING).enumName);
                    }
                }
            });
        }
        //按主档分组
        Map<String, List<GdnCtnrTaskResponse>> collect = list.stream().collect(Collectors.groupingBy(i -> i.getGdnNo()));
        Set<String> keySet = collect.keySet();
        for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext(); ) {
            List<GdnCtnrTaskResponse> gdnCtnrTaskRespons = collect.get(iterator.next());
            GdnCtnrTaskResponse gdnCtnrTaskResponse = gdnCtnrTaskRespons.get(0);
            //每个明细下的车牌列表
            List<String> vehicleNoList = gdnCtnrTaskRespons.stream().map(GdnCtnrTaskResponse::getVehicleNo).distinct().collect(Collectors.toList());
            //每个明细下的实际柜号
            List<String> cntrNoList = gdnCtnrTaskRespons.stream().map(GdnCtnrTaskResponse::getCntrNo).distinct().collect(Collectors.toList());
            //每个明细的状态
            List<Integer> statusList = gdnCtnrTaskRespons.stream().map(GdnCtnrTaskResponse::getStatus).distinct().collect(Collectors.toList());
//            状态：明细行只存在装柜中(6)则显示“装柜中”，否则显示“已拣货”
            if (statusList.size() == 1 && statusList.get(0) == LOADING) {
                gdnCtnrTaskResponse.setHeaderStatusDesc("装柜中");
            } else {
                gdnCtnrTaskResponse.setHeaderStatusDesc("已拣货");
            }
            gdnCtnrTaskResponse.setVehicleNoList(vehicleNoList);
            gdnCtnrTaskResponse.setCntrNoList(cntrNoList);

            //发货单需求数量总和
            BigDecimal orderQtySum = BigDecimal.ZERO;
            //发货单已捡数量总和
            BigDecimal pickQtySum = BigDecimal.ZERO;
            //发货单已装数量总和（主页是取明细的ShipQty汇总）
            BigDecimal shipQtySum = BigDecimal.ZERO;
            //
            //发货单已装数量总和（主页是取明细的ShipQty汇总）
            BigDecimal cmpQtySum = BigDecimal.ZERO;
//            BigDecimal cmpQtySum = BigDecimal.ZERO;

            //按照明细分组
            Map<Integer, List<GdnCtnrTaskResponse>> groupDtl = gdnCtnrTaskRespons.stream().collect(Collectors.groupingBy(GdnCtnrTaskResponse::getGdnDtlId));
            Set<Integer> dtlKeys = groupDtl.keySet();
            for (Iterator<Integer> dtlKey = dtlKeys.iterator(); dtlKey.hasNext(); ) {
                List<GdnCtnrTaskResponse> gdnCtnrTaskResponses = groupDtl.get(dtlKey.next());
//                if (gdnCtnrTaskResponses.get(0).getOrderQtySum() != null) {
//                    orderQtySum = orderQtySum.add(gdnCtnrTaskResponses.get(0).getOrderQtySum());
//                }
                if (gdnCtnrTaskResponses.get(0).getPickQtySum() != null) {
                    pickQtySum = pickQtySum.add(gdnCtnrTaskResponses.get(0).getPickQtySum());
                }
                if (gdnCtnrTaskResponses.get(0).getShipQtySum() != null) {
                    shipQtySum = shipQtySum.add(gdnCtnrTaskResponses.get(0).getShipQtySum());
                }
                //发货单已装数量总和
                List<GdnCtnrTaskResponse> c3 = gdnCtnrTaskResponses.stream().filter(g -> g.getCmpQtySum() != null).collect(Collectors.toList());
                if (!CollectionUtil.isEmpty(c3)) {
                    cmpQtySum = cmpQtySum.add(c3.stream().map(GdnCtnrTaskResponse::getCmpQtySum).reduce((x, y) -> x.add(y)).get());
                }
            }

            gdnCtnrTaskResponse.setShipQtySum(shipQtySum);
            gdnCtnrTaskResponse.setCmpQtySum(cmpQtySum);
//                gdnCtnrTaskResponse.setCmpQtySum(cmpQtySum);
//            List<GdnCtnrTaskResponse> c1 = gdnCtnrTaskRespons.stream().filter(g -> g.getOrderQtySum() != null).collect(Collectors.toList());
//            if (!CollectionUtil.isEmpty(c1)) {
//                BigDecimal orderQtySum = c1.stream().map(GdnCtnrTaskResponse::getOrderQtySum).reduce((x, y) -> x.add(y)).get();
            //2021.04.16  OrderQtySum应该是所有状态明细行的OrderQty汇总，而不仅仅是4567状态
            orderQtySum = gdnHeaderMapper.selectOrderQtySumByGdnNo(gdnCtnrTaskResponse.getGdnNo());
            gdnCtnrTaskResponse.setOrderQtySum(orderQtySum);
//            }
            //发货单已捡数量总和
//            List<GdnCtnrTaskResponse> c2 = gdnCtnrTaskRespons.stream().filter(g -> g.getPickQtySum() != null).collect(Collectors.toList());
//            if (!CollectionUtil.isEmpty(c2)) {
//                BigDecimal pickQtySum = c2.stream().map(GdnCtnrTaskResponse::getPickQtySum).reduce((x, y) -> x.add(y)).get();
            gdnCtnrTaskResponse.setPickQtySum(pickQtySum);
//            }


            result.add(gdnCtnrTaskResponse);

        }
        pagerInfo.setItems(result.stream().skip(gdnCtnrRequest.getSkipCount()).limit(gdnCtnrRequest.getMaxResultCount()).collect(Collectors.toList()));
        pagerInfo.setTotalCount(result.size());
        return pagerInfo;
    }

    /**
     * 发货单明细
     *
     * @param id
     * @return
     */
    public Result<List<GdnDtlDto>> selectDtlById(Long id) {
        List<GdnDtl> gdnDtls = gdnDtlMapper.selectList(new QueryWrapper<>(new GdnDtl().setGdnHeaderId(id)));
        if (CollectionUtil.isEmpty(gdnDtls)) {
            return new Result<>(false, "查无发货单明细", null);
        }
        ArrayList<GdnDtlDto> gdnDtlDtos = new ArrayList<>();
        for (GdnDtl gdnDtl : gdnDtls) {
            String materialCode = null;
            GdnDtlDto gdnDtlDto = new GdnDtlDto();
            Sku sku = skuMapper.selectById(gdnDtl.getSkuId());
            if (sku != null) {
                materialCode = sku.getSkuCode();
            }
            gdnDtlDto.setSoNo(gdnDtl.getSoNo());
            gdnDtlDto.setMaterialCode(materialCode);
            gdnDtlDto.setCustomerModle(gdnDtl.getCustomerModle());
            gdnDtlDto.setOrderQty(gdnDtl.getOrderQty());
            gdnDtlDtos.add(gdnDtlDto);
        }
        return new Result<>(true, "", gdnDtlDtos);
    }

    /**
     * 发货单列表（只查5跟6状态的）
     *
     * @return
     */
    public Result<List<String>> gdnList() {
        Result<List<String>> result = new Result<>();
        List<String> gdnList = gdnDtlMapper.selectGdnList(WAITLOAD, LOADING);
        if (!CollectionUtil.isEmpty(gdnList)) {
            result.setFlag(true);
            result.setMessage("");
            result.setData(gdnList);
        } else {
            result.setFlag(false);
            result.setMessage("查无待装柜或装柜中的发货单");
            result.setData(gdnList);
        }
        return result;
    }

    /**
     * 参考柜号列表（只查5跟6状态的）
     *
     * @return
     */
    public Result<List<String>> refCntrNoList() {
        Result<List<String>> result = new Result<>();
        List<String> refCntrNoList = gdnDtlMapper.selectRefCntrNoList(WAITLOAD, LOADING);
        if (!CollectionUtil.isEmpty(refCntrNoList)) {
            result.setFlag(true);
            result.setMessage("");
            result.setData(refCntrNoList);
        } else {
            result.setFlag(false);
            result.setMessage("查无待装柜或装柜中的参考柜号");
            result.setData(refCntrNoList);
        }
        return result;
    }

    /**
     * 根据发运单明细ID查柜列表
     *
     * @param dtlId
     * @return
     */
    public Result<List<String>> selectCtnrNoListByDtlId(Long dtlId) {
        Result<List<String>> responseResult = new Result<>();
        List<String> ctnrNoList = ctnrLoadingMapper.selectCtnrNoListByDtlId(dtlId);
        if (CollectionUtil.isEmpty(ctnrNoList)) {
            responseResult.setFlag(false);
            responseResult.setMessage("该发货单明细下没有柜号信息");
        } else {
            responseResult.setFlag(true);
            responseResult.setMessage("");
        }
        responseResult.setData(ctnrNoList);
        return responseResult;
    }

    /**
     * 查询发货装运页面
     *
     * @param gdnNo
     * @return
     */
    public Result<GdnCtnrResponse> selectGdnVoByGdnNo(String gdnNo) {
        Result<GdnCtnrResponse> responseResult = new Result<>();
        GdnCtnrResponse gdnCtnrResponse = new GdnCtnrResponse();
        //创建类型的条件构造器
        QueryWrapper<GdnHeader> wrapper = new QueryWrapper<>();
        wrapper.eq("GDN_NO", gdnNo);
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(wrapper);
        gdnCtnrResponse.setGdnHeaderId(gdnHeader.getGdnHeaderId());

        GdnCtnrRequest gdnCtnrRequest = new GdnCtnrRequest();
        gdnCtnrRequest.setGdnNo(gdnNo);
        List<GdnCtnrTaskResponse> list = gdnHeaderMapper.selectGndCtnrListAllStatus(gdnCtnrRequest); //单条精确到柜
        if (CollectionUtil.isEmpty(list)) {
            responseResult.setFlag(false);
            responseResult.setMessage("查无信息");
            return responseResult;
        }
        GdnCtnrTaskResponse gdnCtnrTaskResponse = list.get(0);

        //发货单需求数量总和
        BigDecimal orderQtySum = BigDecimal.ZERO;
        //发货单已捡数量总和
        BigDecimal pickQtySum = BigDecimal.ZERO;
        //发货单已装数量总和
        BigDecimal cmpQtySum = BigDecimal.ZERO;

        //按照明细分组
        Map<Integer, List<GdnCtnrTaskResponse>> groupDtl = list.stream().collect(Collectors.groupingBy(GdnCtnrTaskResponse::getGdnDtlId));
        Set<Integer> dtlKeys = groupDtl.keySet();
        for (Iterator<Integer> dtlKey = dtlKeys.iterator(); dtlKey.hasNext(); ) {
            List<GdnCtnrTaskResponse> gdnCtnrTaskResponses = groupDtl.get(dtlKey.next());
            if (gdnCtnrTaskResponses.get(0).getOrderQtySum() != null) {
                orderQtySum = orderQtySum.add(gdnCtnrTaskResponses.get(0).getOrderQtySum());
            }
            if (gdnCtnrTaskResponses.get(0).getPickQtySum() != null) {
                pickQtySum = pickQtySum.add(gdnCtnrTaskResponses.get(0).getPickQtySum());
            }
        }
        List<GdnCtnrTaskResponse> c3 = list.stream().filter(g -> g.getCmpQtySum() != null).collect(Collectors.toList());
        if (!CollectionUtil.isEmpty(c3)) {
            cmpQtySum = cmpQtySum.add(c3.stream().map(GdnCtnrTaskResponse::getCmpQtySum).reduce((x, y) -> x.add(y)).get());

        }


        gdnCtnrResponse.setCmpQtySum(cmpQtySum);
        gdnCtnrResponse.setOrderQtySum(orderQtySum);
        gdnCtnrResponse.setPickQtySum(pickQtySum);
        gdnCtnrResponse.setGdnNo(gdnNo);
        gdnCtnrResponse.setRefCntrNo(gdnCtnrTaskResponse.getRefCntrNo());
        gdnCtnrResponse.setCntrNo(gdnCtnrTaskResponse.getCntrNo());//实际柜号
        gdnCtnrResponse.setCustomer(gdnCtnrTaskResponse.getCustomer());
        gdnCtnrResponse.setCustomerName(gdnCtnrTaskResponse.getCustomerName());
        gdnCtnrResponse.setShipDate(gdnCtnrTaskResponse.getShipDate());
        //按照明细分组
        Map<Integer, List<GdnCtnrTaskResponse>> dtlGroup = list.stream().collect(Collectors.groupingBy(GdnCtnrTaskResponse::getGdnDtlId));
        Set<Integer> dtlIds = dtlGroup.keySet();
        //---明细列表
        ArrayList<GdnCtnrDtlResponse> gdnCtnrDtlResponses = new ArrayList<>();
        //---SN列表
        ArrayList<GdnDtlGoodsListResponse> gdnDtlGoodsListResponses = new ArrayList<>();
        //---明细行状态列表
        List<Integer> statusList = new ArrayList<>();
        for (Integer dtlId : dtlIds) {
            List<GdnCtnrDtlResponse> list1 = gdnDtlMapper.selectDtlQtyList(dtlId); //单条精确到柜
            if (!CollectionUtil.isEmpty(list1)) {
                GdnCtnrDtlResponse gdnCtnrDtlResponse = list1.get(0);
                //根据明细ID查询,然后设置类型，前面加了分组，不知能否加进去，故新起一个查询
//                GdnDtl gdnDtl = gdnDtlMapper.selectById(gdnCtnrDtlResponse.getDtlId());
//                gdnCtnrDtlResponse.setModleType(gdnDtl.getModleType());
                gdnCtnrDtlResponse.setDtlId(dtlId);
                //发货单明细已扫数量总和
                List<GdnCtnrDtlResponse> d1 = list1.stream().filter(g -> g.getCmpQtySum() != null).collect(Collectors.toList());
                if (!CollectionUtil.isEmpty(d1)) {
                    BigDecimal cmpQtySumWithDtl = d1.stream().map(GdnCtnrDtlResponse::getCmpQtySum).reduce((x, y) -> x.add(y)).get();
                    gdnCtnrDtlResponse.setCmpQtySum(cmpQtySumWithDtl);
                }
                //发货单明细已捡数量总和
                List<GdnCtnrDtlResponse> d2 = list1.stream().filter(g -> g.getPickQty() != null).collect(Collectors.toList());
                if (!CollectionUtil.isEmpty(d2)) {
                    BigDecimal pickQtySumWithDtl = d2.stream().map(GdnCtnrDtlResponse::getPickQty).reduce((x, y) -> x.add(y)).get();
                    gdnCtnrDtlResponse.setPickQty(pickQtySumWithDtl);
                }
                //发货单明细需求数量总和
                List<GdnCtnrDtlResponse> d3 = list1.stream().filter(g -> g.getOrderQty() != null).collect(Collectors.toList());
                if (!CollectionUtil.isEmpty(d3)) {
                    BigDecimal orderQtySumWithDtl = d3.stream().map(GdnCtnrDtlResponse::getOrderQty).reduce((x, y) -> x.add(y)).get();
                    gdnCtnrDtlResponse.setOrderQty(orderQtySumWithDtl);
                }
                if (gdnCtnrDtlResponse.getWhsId() != null) {
                    Warehouse warehouse = queryWhs(gdnCtnrDtlResponse.getWhsId());
                    if (warehouse != null) {
                        gdnCtnrDtlResponse.setWhsCode(warehouse.getWhsCode());
                        gdnCtnrDtlResponse.setWhsName(warehouse.getWhsName());
                    }
                }
                String skuCode = gdnCtnrDtlResponse.getSkuCode();
                //创建类型的条件构造器
                QueryWrapper<Sku> wrapperSku = new QueryWrapper<>();
                wrapperSku.eq("SKU_CODE", skuCode);
                Sku sku = skuMapper.selectOne(wrapperSku);
                gdnCtnrDtlResponse.setSkuDesc(sku.getDescCn());


                statusList.add(gdnCtnrDtlResponse.getStatus());
                gdnCtnrDtlResponses.add(gdnCtnrDtlResponse);
            }

            //---SN列表
            List<GdnDtlGoodsListResponse> snList = gdnDtlMapper.selectDtlSnList(dtlId);
            if (!CollectionUtil.isEmpty(snList)) {
                gdnDtlGoodsListResponses.addAll(snList);
            }
        }
        List<Integer> statusList1 = statusList.stream().distinct().collect(Collectors.toList());
        //状态：明细行只存在装柜中(6)则显示“装柜中”，否则显示“已拣货”

        if (statusList1.size() == 1 && statusList1.get(0) == LOADING) {
            gdnCtnrResponse.setHeaderStatusDesc("装柜中");
        } else {
            gdnCtnrResponse.setHeaderStatusDesc("已拣货");
        }
        //设置明细和SN列表
        gdnCtnrResponse.setGdnCtnrDtlResponseList(gdnCtnrDtlResponses);
        gdnCtnrResponse.setGoodsDtoList(gdnDtlGoodsListResponses);
        responseResult.setData(gdnCtnrResponse);
        responseResult.setMessage("");
        responseResult.setFlag(true);
        return responseResult;
    }

    /**
     * 根据柜号查明细
     *
     * @param dtlId
     * @param ctnrNo
     * @return
     */
    public Result<GdnCtnrDtlGoodsResponse> selectCtnrDtlByCtnrNo(Long dtlId, String ctnrNo) {
        Result<GdnCtnrDtlGoodsResponse> responseResult = new Result<>();
        GdnCtnrDtlGoodsResponse gdnCtnrDtlGoodsResponse = new GdnCtnrDtlGoodsResponse();
        if (ctnrNo == null || ctnrNo.isEmpty()) {
            //如果没传柜号，则默认返回1号柜
            List<String> ctnrNoList = ctnrLoadingMapper.selectCtnrNoListByDtlId(dtlId);
            if (CollectionUtil.isEmpty(ctnrNoList)) {
                responseResult.setFlag(false);
                responseResult.setMessage("该发货单明细下没有柜号信息");

                return responseResult;
            } else {
                ctnrNo = ctnrNoList.get(0);
            }
        }
        gdnCtnrDtlGoodsResponse = ctnrLoadingMapper.selectGdnDtlByNo(dtlId, ctnrNo);
        if (gdnCtnrDtlGoodsResponse.getWhsId() != null) {
            Warehouse warehouse = queryWhs(gdnCtnrDtlGoodsResponse.getWhsId());
            if (warehouse != null) {
                gdnCtnrDtlGoodsResponse.setWhsCode(warehouse.getWhsCode());
                gdnCtnrDtlGoodsResponse.setWhsName(warehouse.getWhsName());
            }
        }
        responseResult.setData(gdnCtnrDtlGoodsResponse);
        responseResult.setFlag(true);
        responseResult.setMessage("");
        return responseResult;
    }

    /**
     * 校验SN是否归可装到当前发运单+实际柜号。
     * 校验：
     * ①	前端校验SN是否在当前【SN明细列表】，不允许重复扫描，
     * ②	查询货品清单(WMS_GOODS)，校验SN状态(status) =已下架(5) ，并且物料是正确的。
     * ③	校验SN是否归属于当前发货单明细，查询是否存在于，拣货任务明细货品清单表(WMS_PICKTASK_DTL_GOODS)。
     * ④	若第3点不存在，则校验SN是否归属于 与当前发货单 在相同 发货通知单 的发货单，并且销售订单号、物料编
     * 码相同的SN (根据 当前发货单的“来源发货通知单”、“销售订单”、“物料编码”，找到可以跨柜的发货单明细ID，再
     * 根据发货单明细ID，查询SN是否存在于，拣货任务明细货品清单表(WMS_PICKTASK_DTL_GOODS))。
     * <p>
     * TODO ⑤： 在第③、④点不符合，但是SN是归属于 与当前发货单 在相同 发货通知单 的发货单。但销售订单号、物料编码不在当前发货单，在其他发货单有需求，并且那个发货单明细行状态是已发运(7)。
     * 则前端弹出用户提示框，提示“条码的物料不属于当前发货单，是否进行需求数量转移？”。
     * 用户确认之后，则把需求数量从原单转移到当前单据，即发货单明细表(WMS_GDN_DTL) 把原发货单明细行复制一份到当前发货单上，订单数量(ORDER_QTY)=0，发货数量(SHIP_QTY)从0开始累加。
     * 详情见如下数据推演：
     * <p>
     * 不需要校验 批次号是否准确，因为拣货的时候，已经校验了。
     * TODO
     * <p>
     * 扫描SN校验
     *
     * @param sns
     * @return
     */
    public Result<SnAndQtyRequest> batchCheckSn(List<String> sns, GdnDtl gdnDtl,GdnHeader gdnHeader,boolean isCommit) {
        //查询货品清单(WMS_GOODS)，校验SN状态(status) =已下架(5) ，并且物料是正确的(skuID是否是这个发货单明细的)。
        log.info("ZYF++"+sns);
        Result<SnAndQtyRequest> result = new Result<>();
        Long gdnHeaderId = gdnDtl.getGdnHeaderId();//表头ID
        Long dtlId = gdnDtl.getGdnDtlId();//明细ID
        String gdnNo = gdnHeader.getGdnNo();
        List<GdnDtl> gdnDtlList = gdnDtlMapper.selectByHeaderId(gdnHeaderId);
        outer:
        for(String sn : sns){

            result.setFlag(false);
            Goods goods = goodsMapper.selectBySn(sn);
            if (goods == null) {
                result.setMessage("SN不存在:"+sn);
                break;
            }
            if (GoodsStatusEnum.HAS_DOWN.getCode() != goods.getStatus()) {
                result.setMessage("SN不是已拣选状态:"+sn);
                break;
            }
            if (goods.getSkuId() == null) {
                result.setMessage("SN查无对应货品:"+sn);
                break;
            }

            //需要优化项-----
            //⑤

            int shipmentedCode = GdnDtlStatusEnum.SHIPMENTED.getCode();//已发运————————————————————7

            String sourceShipNotice = gdnHeader.getSourceShipNotice();//当前入参的来源发货通知单

            String goodsSoNo = goods.getSoNo();//条码的销售订单
            if (goodsSoNo == null) {
                Shift.fatal(StatusCode.DATA_ERR, "条码:"+sn+"下未查询到销售单号，请核实数据是否真实");
            }

            Integer goodsSkuId = goods.getSkuId();//goods的物料ID
            //条件1：销售订单号、物料编码不在【当前】发货单
            //条件1标识
            boolean flag = (!goodsSoNo.equals(gdnDtl.getSoNo())) || (!goodsSkuId.equals(gdnDtl.getSkuId()));

            //条件2：但销售订单号、物料编码不在当前发货单，在其他发货单有需求，并且那个发货单明细行状态是已发运(7)。
            GdnDtl gdnDtlChange = gdnDtlMapper.selectBySoSkuIdSourceShipNotice(goodsSoNo, goodsSkuId, sourceShipNotice, shipmentedCode, sn);
            //条件2标识
            boolean statusFlag = (gdnDtlChange != null);

            boolean groupFlag = (flag && statusFlag);

            //创建类型的条件构造器
            QueryWrapper<PicktaskDtlGoods> wrapper = new QueryWrapper<>();
            wrapper.eq("GOODS_SN", sn);
            wrapper.orderByDesc("CREATE_TIME");
            List<PicktaskDtlGoods> picktaskDtlGoods = picktaskDtlGoodsMapper.selectList(wrapper);
            PicktaskDtlGoods dtlGoods = picktaskDtlGoods.get(0);

            PicktaskDtl picktaskDtl = picktaskDtlMapper.selectById(dtlGoods.getPicktaskDtlId());

            //TODO ③ 校验SN是否归属于当前发货单明细，查询是否存在于，拣货任务明细货品清单表(WMS_PICKTASK_DTL_GOODS)。
            //int i = picktaskDtlGoodsMapper.selectCountBySnAndDocDtlId(sn, dtlId);
            //TODO
            // 若第③点不存在，则校验SN是否归属于 与当前发货单 在相同 发货通知单 的发货单，
            // 并且销售订单号、物料编码相同的SN
            // (根据 当前发货单的“来源发货通知单”、“销售订单”、“物料编码”，找到可以跨柜的发货单明细ID，再根据发货单明细ID，
            // 查询SN是否存在于，拣货任务明细货品清单表(WMS_PICKTASK_DTL_GOODS))。
            //if (i == 0) {

            //该SN的来源单号集合
            List<String> sourceShipNoticeList = gdnDtlGoodsMapper.selectSourceBySn(sn);
            //当前dtlId的来源单号
            String sourceShipnotice = gdnDtlGoodsMapper.selectSourceByDtlId(dtlId);

            boolean sourceFlag = !sourceShipNoticeList.contains(sourceShipnotice);
            if (sourceFlag) {
                result.setMessage("条码:"+sn+"的物料不属于该发货单明细或与该发货单明细来源单号不同");
                break;
            } else {
                //相同来源通知单
                //查看SN是否属于当前发货单
                boolean flagSameShipNotic = false;


                GdnDtl gdnDtlSource = gdnDtlMapper.selectById(picktaskDtl.getDocDtlId());
                GdnHeader gdnHeaderSource = gdnHeaderMapper.selectById(gdnDtlSource.getGdnHeaderId());

                String gdnNoGoods = gdnHeaderSource.getGdnNo();
                //相同出库单
                if (gdnNoGoods.equals(gdnNo)) {

                /*for (GdnDtl dtl : gdnDtlList) {
                    if (goodsSkuId.equals(dtl.getSkuId()) && goodsSoNo.equals(dtl.getSoNo())
                            && goods.getWhsId().equals(dtl.getWhsId())) {
                        if (dtlId.equals(dtl.getGdnDtlId())) {
                            continue;
                        } else {
                            result.setMessage("条码:"+sn+"属于相同发货单的其他明细行中，请转移至对应的发货明细中操作");
                            return result;
                        }
                    }
                }*/
                    //未找到相同的【来源单号】
                    if (groupFlag) {
                        result.setMessage("条码:"+sn+"的物料不属于当前发货单，是否进行需求数量转移");
                        result.setCode(2);
                        break;
                    }
                    //相同来源通知单,且SN属于当前发货单
                    //TODO 先查看是否在本单中(根据SKU_ID和SO)，本单中有就提示滚去本单的明细扫描，否则转移
                    if(!gdnDtl.getSkuId().equals(goods.getSkuId())){
                        result.setMessage("条码:"+sn+"物料和当前明细行物料不一致，请转移至对应的发货明细中操作");
                        break;
                    }
                    //比较扫描条码的物料、销售单号、仓库是否在当前明细行有对应明细数据
                    QueryWrapper<GdnDtl> queryWrapper = new QueryWrapper<>();
                    queryWrapper.setEntity(new GdnDtl().
                            setGdnHeaderId(gdnHeaderId).
                            setSkuId(goodsSkuId).
                            setSoNo(goodsSoNo).
                            setWhsId(goods.getWhsId()));
                    List<GdnDtl> gdnDtlSame = gdnDtlMapper.selectList(queryWrapper);
                    if(gdnDtlSame==null){//没有符合条码的明细行，需求转移
                        //判断当前行物料和goods是否一致 不一致提示去对应物料行里面扫描

                        result.setMessage("条码:"+sn+"仓库和当前发货单明细不一致，是否进行需求数量转移");
                        result.setCode(2);
                        break;
                    }
                    List<Long> dtlIdList = gdnDtlSame.stream().map(s -> s.getGdnDtlId()).collect(Collectors.toList());
                    //比较查询出来的明细行和现在所在的明细行ID一不一致

                    if (!dtlIdList.contains(dtlId)) {
                        result.setMessage("条码:"+sn+"属于相同发货单的其他明细行中，请转移至对应的发货明细中操作");
                        break;
                    }
                } else {
                    //相同来源通知单,但SN在另外的发货通知单

                    if (statusFlag) {//其实就是查询是否对应行是不是已经发运
                        //TODO 再校验相同发货单下是否有刚转移的明细行，没有则提示转移，有 则提示滚到对应的明细行中扫描
                        //GDN0061002
                        //创建类型的条件构造器
                        QueryWrapper<GdnDtl> wrapperDtl = new QueryWrapper<>();
                        wrapperDtl.eq("GDN_HEADER_ID", gdnHeader.getGdnHeaderId());
                        wrapperDtl.eq("SKU_ID", goodsSkuId);
                        wrapperDtl.eq("SO_NO", goodsSoNo);
                        wrapperDtl.eq("WHS_ID", goods.getWhsId());
                        List<GdnDtl> gdnDtls = gdnDtlMapper.selectList(wrapperDtl);

                        List<Long> dtlIdList = gdnDtls.stream().map(s -> s.getGdnDtlId()).collect(Collectors.toList());

                        if (gdnDtls.size()>0) {
                            if (!dtlIdList.contains(dtlId)) {
                                result.setMessage("条"+sn+"码不属于当前发货单明细，请转移至相应的明细操作");
                                break;
                            }
                        } else {
                            result.setMessage("条码"+sn+"的物料不属于当前发货单，是否进行需求数量转移");
                            result.setCode(2);
                            break;
                        }

                    }else {
                        log.info("相同来源通知单,但SN不属于当前发货单");
                        if ((!goodsSoNo.equals(gdnDtl.getSoNo()))||(!goodsSkuId.equals(gdnDtl.getSkuId()))) {
//                            result.setMessage("该条码属于相同来源通知单,但SN不属于当前发货单,请到对应的发货单据明细中操作");
                            result.setMessage("条码"+sn+"属于相同来源通知单，不属于该发货单，其对应的发货单明细状态非【已发运】");
                            break;
                        }
                        //先不管了，先在这里加需求转移
                        //加个仓库校验

                        for (GdnDtl dtl : gdnDtlList) {
                            if (goodsSkuId.equals(dtl.getSkuId()) && goodsSoNo.equals(dtl.getSoNo())
                                    && goods.getWhsId().equals(dtl.getWhsId())) {
                                if (dtlId.equals(dtl.getGdnDtlId())) {
                                    continue;
                                } else {
                                    result.setMessage("条码:"+sn+"属于相同发货单的其他明细行中，请转移至对应的发货明细中操作");
                                    break outer;
                                }
                            }
                        }

                        if(!gdnDtl.getWhsId().equals(goods.getWhsId())){
                            result.setMessage("条码"+sn+"的仓库和当前发货单明细仓库不一致，是否进行需求数量转移");
                            result.setCode(2);
                            break outer;
                        }

//                        if (!goodsSkuId.equals(gdnDtl.getSkuId())) {
//                            result.setMessage("该条码属于相同来源通知单,但SN的物料与该发货单明细的物料不同，请到对应的单据明细操作");
//                            return result;
//                        }
                    }
                }
            }


            //将扫描的SN加入到缓存----------------------------starrt
            GoodsDto goodsDto = new GoodsDto();
            goodsDto.setGoodsSn(sn);
            goodsDto.setQty(goods.getQty());
            try {
                insertUpdateCache(dtlId, gdnHeader.getGdnNo(), goodsDto,isCommit);
            }catch (SystemException e){
                Shift.fatal(StatusCode.BUSINESS_ERR,e.getMessage());
            }

            //将扫描的SN加入到缓存------------------------------end

            result.setFlag(true);
            result.setMessage("");
            SnAndQtyRequest snAndQty = new SnAndQtyRequest();
            snAndQty.setGoodsSN(sn);
            snAndQty.setQty(goods.getQty());
            result.setData(snAndQty);
            continue ;
        }
        return  result;
    }


    public Result<SnAndQtyRequest> checkSn(String sn, Long dtlId,boolean isCommit) {
        //查询货品清单(WMS_GOODS)，校验SN状态(status) =已下架(5) ，并且物料是正确的(skuID是否是这个发货单明细的)。
        log.info("ZYF++"+sn);
        Result<SnAndQtyRequest> result = new Result<>();
        result.setFlag(false);
        Goods goods = goodsMapper.selectBySn(sn);
        if (goods == null) {
            result.setMessage("SN不存在:"+sn);
            return result;
        }
        if (GoodsStatusEnum.HAS_DOWN.getCode() != goods.getStatus()) {
            result.setMessage("SN不是已拣选状态:"+sn);
            return result;
        }
        if (goods.getSkuId() == null) {
            result.setMessage("SN查无对应货品:"+sn);
            return result;
        }

        //需要优化项-----
        GdnDtl gdnDtl = gdnDtlMapper.selectById(dtlId);
        //⑤
        Long gdnHeaderId = gdnDtl.getGdnHeaderId();
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);
        String gdnNo = gdnHeader.getGdnNo();
        int shipmentedCode = GdnDtlStatusEnum.SHIPMENTED.getCode();//已发运————————————————————7

        String sourceShipNotice = gdnHeader.getSourceShipNotice();//当前入参的来源发货通知单

        String goodsSoNo = goods.getSoNo();//条码的销售订单
        if (goodsSoNo == null) {
            Shift.fatal(StatusCode.DATA_ERR, "条码:"+sn+"下未查询到销售单号，请核实数据是否真实");
        }

        Integer goodsSkuId = goods.getSkuId();//goods的物料ID
        //条件1：销售订单号、物料编码不在【当前】发货单
        //条件1标识
        boolean flag = (!goodsSoNo.equals(gdnDtl.getSoNo())) || (!goodsSkuId.equals(gdnDtl.getSkuId()));

        //条件2：但销售订单号、物料编码不在当前发货单，在其他发货单有需求，并且那个发货单明细行状态是已发运(7)。
        GdnDtl gdnDtlChange = gdnDtlMapper.selectBySoSkuIdSourceShipNotice(goodsSoNo, goodsSkuId, sourceShipNotice, shipmentedCode, sn);
        //条件2标识
        boolean statusFlag = (gdnDtlChange != null);

        boolean groupFlag = (flag && statusFlag);

        //创建类型的条件构造器
        QueryWrapper<PicktaskDtlGoods> wrapper = new QueryWrapper<>();
        wrapper.eq("GOODS_SN", sn);
        wrapper.orderByDesc("CREATE_TIME");
        List<PicktaskDtlGoods> picktaskDtlGoods = picktaskDtlGoodsMapper.selectList(wrapper);
        PicktaskDtlGoods dtlGoods = picktaskDtlGoods.get(0);

        PicktaskDtl picktaskDtl = picktaskDtlMapper.selectById(dtlGoods.getPicktaskDtlId());
        //2021-12-07 解决条码拣料和出库的时候仓库不一致跳过校验的bug  校验一下仓库，拣货任务里面没有仓库 从预配表里面取仓库
        DocPreAloc docPreAloc = docPreAlocService.selectByPk(picktaskDtl.getDocPreAlocId());
        if(!docPreAloc.getWhsId().equals(goods.getWhsId())){
            Shift.fatal("条码【"+goods.getGoodsSn()+"】拣料的仓库和当前所在的仓库不一致，请联系相关运维人员");
        }

        //TODO ③ 校验SN是否归属于当前发货单明细，查询是否存在于，拣货任务明细货品清单表(WMS_PICKTASK_DTL_GOODS)。
        //int i = picktaskDtlGoodsMapper.selectCountBySnAndDocDtlId(sn, dtlId);
        //TODO
        // 若第③点不存在，则校验SN是否归属于 与当前发货单 在相同 发货通知单 的发货单，
        // 并且销售订单号、物料编码相同的SN
        // (根据 当前发货单的“来源发货通知单”、“销售订单”、“物料编码”，找到可以跨柜的发货单明细ID，再根据发货单明细ID，
        // 查询SN是否存在于，拣货任务明细货品清单表(WMS_PICKTASK_DTL_GOODS))。
        //if (i == 0) {

        //该SN的来源单号集合
        List<String> sourceShipNoticeList = gdnDtlGoodsMapper.selectSourceBySn(sn);
        //当前dtlId的来源单号
        String sourceShipnotice = gdnDtlGoodsMapper.selectSourceByDtlId(dtlId);

        boolean sourceFlag = !sourceShipNoticeList.contains(sourceShipnotice);
        if (sourceFlag) {
            result.setMessage("条码:"+sn+"的物料不属于该发货单明细或与该发货单明细来源单号不同");
            return result;
        } else {
            //相同来源通知单
            //查看SN是否属于当前发货单
            boolean flagSameShipNotic = false;


            GdnDtl gdnDtlSource = gdnDtlMapper.selectById(picktaskDtl.getDocDtlId());
            GdnHeader gdnHeaderSource = gdnHeaderMapper.selectById(gdnDtlSource.getGdnHeaderId());

            String gdnNoGoods = gdnHeaderSource.getGdnNo();
            //相同出库单
            if (gdnNoGoods.equals(gdnNo)) {

                /*for (GdnDtl dtl : gdnDtlList) {
                    if (goodsSkuId.equals(dtl.getSkuId()) && goodsSoNo.equals(dtl.getSoNo())
                            && goods.getWhsId().equals(dtl.getWhsId())) {
                        if (dtlId.equals(dtl.getGdnDtlId())) {
                            continue;
                        } else {
                            result.setMessage("条码:"+sn+"属于相同发货单的其他明细行中，请转移至对应的发货明细中操作");
                            return result;
                        }
                    }
                }*/
                //未找到相同的【来源单号】
                if (groupFlag) {
                    result.setMessage("条码:"+sn+"的物料不属于当前发货单，是否进行需求数量转移");
                    result.setCode(2);
                    return result;
                }
                //相同来源通知单,且SN属于当前发货单
                //TODO 先查看是否在本单中(根据SKU_ID和SO)，本单中有就提示滚去本单的明细扫描，否则转移
                if(!gdnDtl.getSkuId().equals(goods.getSkuId())){
                    result.setMessage("条码:"+sn+"物料和当前明细行物料不一致，请转移至对应的发货明细中操作");
                    return result;
                }
                //比较扫描条码的物料、销售单号、仓库是否在当前明细行有对应明细数据
                QueryWrapper<GdnDtl> queryWrapper = new QueryWrapper<>();
                queryWrapper.setEntity(new GdnDtl().
                        setGdnHeaderId(gdnHeaderId).
                        setSkuId(goodsSkuId).
                        setSoNo(goodsSoNo).
                        setWhsId(goods.getWhsId()));
                List<GdnDtl> gdnDtlSame = gdnDtlMapper.selectList(queryWrapper);
                if(gdnDtlSame==null){//没有符合条码的明细行，需求转移
                    //判断当前行物料和goods是否一致 不一致提示去对应物料行里面扫描

                    result.setMessage("条码:"+sn+"仓库和当前发货单明细不一致，是否进行需求数量转移");
                    result.setCode(2);
                    return result;
                }
                List<Long> dtlIdList = gdnDtlSame.stream().map(s -> s.getGdnDtlId()).collect(Collectors.toList());
                //比较查询出来的明细行和现在所在的明细行ID一不一致

                if (!dtlIdList.contains(dtlId)) {
                    result.setMessage("条码:"+sn+"属于相同发货单的其他明细行中，请转移至对应的发货明细中操作");
                    return result;
                }
            } else {
                //相同来源通知单,但SN在另外的发货通知单

                if (statusFlag) {//其实就是查询是否对应行是不是已经发运
                    //TODO 再校验相同发货单下是否有刚转移的明细行，没有则提示转移，有 则提示滚到对应的明细行中扫描
                    //GDN0061002
                    //创建类型的条件构造器
                    QueryWrapper<GdnDtl> wrapperDtl = new QueryWrapper<>();
                    wrapperDtl.eq("GDN_HEADER_ID", gdnHeader.getGdnHeaderId());
                    wrapperDtl.eq("SKU_ID", goodsSkuId);
                    wrapperDtl.eq("SO_NO", goodsSoNo);
                    wrapperDtl.eq("WHS_ID", goods.getWhsId());
                    List<GdnDtl> gdnDtls = gdnDtlMapper.selectList(wrapperDtl);

                    List<Long> dtlIdList = gdnDtls.stream().map(s -> s.getGdnDtlId()).collect(Collectors.toList());

                    if (gdnDtls.size()>0) {
                        if (!dtlIdList.contains(dtlId)) {
                            result.setMessage("条"+sn+"码不属于当前发货单明细，请转移至相应的明细操作");
                            return result;
                        }
                    } else {
                        result.setMessage("条码"+sn+"的物料不属于当前发货单，是否进行需求数量转移");
                        result.setCode(2);
                        return result;
                    }

                }else {
                    log.info("相同来源通知单,但SN不属于当前发货单");
                    if ((!goodsSoNo.equals(gdnDtl.getSoNo()))||(!goodsSkuId.equals(gdnDtl.getSkuId()))) {
//                            result.setMessage("该条码属于相同来源通知单,但SN不属于当前发货单,请到对应的发货单据明细中操作");
                        result.setMessage("条码"+sn+"属于相同来源通知单，不属于该发货单，其对应的发货单明细状态非【已发运】");
                        return result;
                    }
                    //先不管了，先在这里加需求转移
                    //加个仓库校验
                    List<GdnDtl> gdnDtlList = gdnDtlMapper.selectByHeaderId(gdnHeaderId);
                    for (GdnDtl dtl : gdnDtlList) {
                        if (goodsSkuId.equals(dtl.getSkuId()) && goodsSoNo.equals(dtl.getSoNo())
                                && goods.getWhsId().equals(dtl.getWhsId())) {
                            if (dtlId.equals(dtl.getGdnDtlId())) {
                                continue;
                            } else {
                                result.setMessage("条码:"+sn+"属于相同发货单的其他明细行中，请转移至对应的发货明细中操作");
                                return result;
                            }
                        }
                    }

                    if(!gdnDtl.getWhsId().equals(goods.getWhsId())){
                        result.setMessage("条码"+sn+"的仓库和当前发货单明细仓库不一致，是否进行需求数量转移");
                        result.setCode(2);
                        return result;
                    }

//                        if (!goodsSkuId.equals(gdnDtl.getSkuId())) {
//                            result.setMessage("该条码属于相同来源通知单,但SN的物料与该发货单明细的物料不同，请到对应的单据明细操作");
//                            return result;
//                        }
                }
            }
        }


        //将扫描的SN加入到缓存----------------------------starrt
        GoodsDto goodsDto = new GoodsDto();
        goodsDto.setGoodsSn(sn);
        goodsDto.setQty(goods.getQty());
        try {
            insertUpdateCache(dtlId, gdnHeader.getGdnNo(), goodsDto,isCommit);
        }catch (SystemException e){
            Shift.fatal(StatusCode.BUSINESS_ERR,e.getMessage());
        }

        //将扫描的SN加入到缓存------------------------------end

        result.setFlag(true);
        result.setMessage("");
        SnAndQtyRequest snAndQty = new SnAndQtyRequest();
        snAndQty.setGoodsSN(sn);
        snAndQty.setQty(goods.getQty());
        result.setData(snAndQty);




        return result;
    }


    /**
     * 校验SN是否归属于当前发货单明细,若不属于，
     * 则校验SN是否处于相同来源发货通知单，相同物料，相同销售订单，是否存在于拣货任务表
     *
     * @param sn
     * @param dtlId
     * @return
     */
    private String checkSnSource(String sn, Long dtlId) {
        GdnDtlGoods gdnDtlGoods = gdnDtlGoodsMapper.selectOne(new QueryWrapper<>(new GdnDtlGoods().setGdnDtlId(dtlId).setGoodsSn(sn)));
        GdnDtl gdnDtl = gdnDtlMapper.selectById(dtlId);
        if (gdnDtlGoods == null) {
            /**
             * 若第②点不存在，则校验SN是否归属于 与当前发货单 在相同 发货通知单 的发货单，并且销售订单号、物料编
             * 码相同的SN (根据 当前发货单的“来源发货通知单”、“销售订单”、“物料编码”，找到可以跨柜的发货单明细ID，再
             * 根据发货单明细ID，查询SN是否存在于，拣货任务明细货品清单表(WMS_PICKTASK_DTL_GOODS))。
             */
            //查询SN来源发货通知单
//            String sourceShipNotice = gdnDtlGoodsMapper.selectSourceBySn(sn);
            String sourceShipNotice = gdnDtlGoodsMapper.selectSourceBySnAndDtlId(sn, dtlId);


            if (sourceShipNotice == null) {
                return "SN不属于当前发货单明细";
            }

            String sourceShipNoticeWithDtlId = gdnDtlMapper.selectSourceByDtlId(dtlId);
            if (!sourceShipNotice.equals(sourceShipNoticeWithDtlId)) {
                return "SN既不属于当前发货单明细，也不属于相同来源发货通知单";
            }

            //查找相同来源发货通知单,相同销售订单，物料编码的发货单明细
            String soNo = gdnDtl.getSoNo();
            Integer skuId = gdnDtl.getSkuId();
            List<Integer> dtlList = gdnDtlMapper.selectBySourceShipNotice(sourceShipNotice, soNo, skuId, dtlId);
            if (CollectionUtil.isEmpty(dtlList)) {
                return "SN查无相同来源的发货通知单明细";
            }
            //通过明细列表查询SN列表
            List<String> snList = picktaskDtlGoodsMapper.selectGoodsListByGdnDtlIds(dtlList);
            //判断SN是否处于相同来源发货通知单
            if (!snList.contains(sn)) {
                return "SN既不属于当前发货单明细，也不属于相同来源发货通知单";
            }
        }

        return "";
    }

    /**
     * 提交
     *
     * @param gdnCtnrCommitRequest
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<?> commit(GdnCtnrCommitRequest gdnCtnrCommitRequest, String loginName) throws Exception {
        String ctnrNo = gdnCtnrCommitRequest.getCtnrNo(); //实际柜号
        Long gdnDtlId = gdnCtnrCommitRequest.getGdnDtlId(); //明细ID
        List<SnAndQtyRequest> snList = gdnCtnrCommitRequest.getSnList(); //条码列表
        //TODO 添加重复SN过滤
        int commitSize = snList.size();//提交时的size

        Set<String> hashSetSn = new HashSet<>();
        for (SnAndQtyRequest request : snList) {
            hashSetSn.add(request.getGoodsSN());
        }
        if (hashSetSn.size() != commitSize) {
            Shift.fatal(StatusCode.OPERATE_ERR,"请勿提交重复的条码");
        }
        //入参SN集合
        List<String> snOriginalList = snList.stream().map(s -> s.getGoodsSN()).collect(Collectors.toList());
        //初始SN信息
        //List<Goods> goodsOriginalList = goodsService.selectListByListSn(snOriginalList);
        List<Goods> goodsOriginalList = goodsMapper.selectBySNs(snOriginalList);

        BigDecimal thisQty = snList.stream().map(SnAndQtyRequest::getQty).reduce((x, y) -> x.add(y)).get();//这次提交的总数量

        GdnDtl gdnDtl = gdnDtlMapper.selectById(gdnDtlId);

        Long gdnHeaderId = gdnDtl.getGdnHeaderId();
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);

        //校验
//        ①	校验装柜单(WMS_CTNR_LOADING)状态(STATUS)=完成(3)，则不允许重复提交。
        CtnrLoading ctnrLoading = ctnrLoadingMapper.selectOne(new QueryWrapper<>(new CtnrLoading().setGdnDtlId(gdnDtlId).setCntrNo(ctnrNo)));
        if (ctnrLoading == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无该装柜单");
        }
        if (CtnrLoadingStatusEnum.FINISH.code == ctnrLoading.getStatus()) {
            return new Result<>(false, "装柜单已完成");
        }
        int i = 0 ;
       //提交慢 优化
//        long time1 = new Date().getTime();
//        for (SnAndQtyRequest snAndQtyRequest : snList) {
//            //        ②	校验全部产品SN状态，是否都允许装运：查询货品清单(WMS_GOODS)，校验SN状态(status) =已下架(5)
//            Result<SnAndQtyRequest> checkResult = checkSn(snAndQtyRequest.getGoodsSN(), gdnDtlId,true);
//            log.info("校验第====>"+(i++)+"SN");
//            if (!checkResult.getMessage().equals("")) {
//
//                //提交后删除缓存
//                //deleteSnCacheList(gdnDtlId, gdnHeader.getGdnNo(), null);
//                //提交后删除缓存
//
//                return new Result<>(false, snAndQtyRequest.getGoodsSN() + checkResult.getMessage());
//            }
//        }
//        long time2 = new Date().getTime();
//        log.info("更改前校验时间===>"+(time2-time1));
        Result<SnAndQtyRequest> checkResult = batchCheckSn(snOriginalList, gdnDtl, gdnHeader, true);
        if (!checkResult.getMessage().equals("")) {

                //提交后删除缓存
                //deleteSnCacheList(gdnDtlId, gdnHeader.getGdnNo(), null);
                //提交后删除缓存

                return new Result<>(false, checkResult.getMessage());
            }
        //log.info("更改后校验时间===>"+(new Date().getTime()-time2));
        //Shift.fatal("业务回滚");

        //写数
        /**
         * ①	变更货品清单(WMS_GOODS)的本次提交SN状态(status)=已出库(8),
         *      SN移到历史表 WMS_GOODS_HIS
         * ②	写库存交易凭证，出库凭证(T03)，源单号是出库单 调服务
         * ③	调库存更新服务，变更库存。库区库存表(WMS_INV_ZONE)、库位库存表(WMS_INV_LOC)
         * ④	更新 装柜单(WMS_CTNR_LOADING)的
         *      状态(STATUS) =进行中(2);
         *      实际装柜数量(CMP_QTY) +=本次装运数量。
         * ⑤	插入全部SN到 装柜单货品清单(WMS_CTNR_LOADING_GOODS) 表
         * ⑥	更新 发货单明细表(WMS_GDN_DTL)，若有跨柜装运，则根据SN找到对应的发货单明细更新。
         * ⑦	插入全部SN到发货通知单明细货品清单(WMS_GDN_DTL_GOODS) 表，若有跨柜装运，则根据SN找到对应的发货单明细插入清单。
         * ⑧	更新 发货单(WMS_GDN_HEADER)的
         * 状态(STATUS) = 进行中(3)
         */
        //(1)变更货品清单(WMS_GOODS)的本次提交SN状态(status)=已出库(8)
        List<String> sns = snList.stream().map(SnAndQtyRequest::getGoodsSN).collect(Collectors.toList());
        goodsMapper.batchUpdateStatus(sns, GoodsStatusEnum.HAS_CHUKU.code);

        //(2)写库存交易凭证，出库凭证(T03)，源单号是出库单（发货单） 调服务
        //(3)调库存更新服务，变更库存。库区库存表(WMS_INV_ZONE)、库位库存表(WMS_INV_LOC)
        //*****************根据SN查询其真正的发货单和明细***************//
        ArrayList<GdnDtlGoodsSimpleReq> simpleReqs = new ArrayList<>();
        for (SnAndQtyRequest snAndQtyRequest : snList) {
            //通过拣货任务明细里找到对应单据明细（DOC_DTL_ID）-> 找到对应发货单
//            PicktaskDtlGoods picktaskDtlGoods = picktaskDtlGoodsMapper.selectOne(new QueryWrapper<>(new PicktaskDtlGoods().setGoodsSn(snAndQtyRequest.getGoodsSN())));
//            if (picktaskDtlGoods == null) {
//                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无对应拣货任务");
//            }
//            PicktaskDtl picktaskDtl = picktaskDtlMapper.selectOne(new QueryWrapper<>(new PicktaskDtl().setPicktaskDtlId(picktaskDtlGoods.getPicktaskDtlId())));
//            if (picktaskDtl == null) {
//                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无对应拣货任务");
//            }
//            GdnDtl g = gdnDtlMapper.selectById(picktaskDtl.getDocDtlId());
//            if (g == null) {
//                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "拣货任务查无对应发货单明细");
//            }
//他喵的，上面原来是有的——————————————————YWB

//            GdnHeader gdnHeader = gdnHeaderMapper.selectById(g.getGdnHeaderId());
//            GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnDtl.getGdnHeaderId());
            GdnDtlGoodsSimpleReq gdnDtlGoodsSimpleReq = new GdnDtlGoodsSimpleReq();
//            gdnDtlGoodsSimpleReq.setGdnDtlId(g.getGdnDtlId());
            gdnDtlGoodsSimpleReq.setGdnDtlId(gdnDtl.getGdnDtlId());
            gdnDtlGoodsSimpleReq.setGdnHeaderId(gdnHeader.getGdnHeaderId());
            gdnDtlGoodsSimpleReq.setGdnNo(gdnHeader.getGdnNo());
            gdnDtlGoodsSimpleReq.setSn(snAndQtyRequest.getGoodsSN());
            gdnDtlGoodsSimpleReq.setSnQty(snAndQtyRequest.getQty());
            simpleReqs.add(gdnDtlGoodsSimpleReq);
        }
        //按照发货单号分组
        Map<String, List<GdnDtlGoodsSimpleReq>> map = simpleReqs.stream().collect(Collectors.groupingBy(GdnDtlGoodsSimpleReq::getGdnNo));
        Set<String> set = map.keySet();
        //按发货单号为单位写入库存交易凭证和更新库位库存
        for (Iterator iterator = set.iterator(); iterator.hasNext(); ) {
            List<GdnDtlGoodsSimpleReq> gdnDtlGoodsSimpleReqs = map.get(iterator.next());
            GdnDtlGoodsSimpleReq req = gdnDtlGoodsSimpleReqs.get(0);
            String gdnNo = req.getGdnNo();
            List<String> singleGroupSn = gdnDtlGoodsSimpleReqs.stream().map(GdnDtlGoodsSimpleReq::getSn).collect(Collectors.toList());
            //单组本次扫描SN QTY总和
            BigDecimal gorupQty = gdnDtlGoodsSimpleReqs.stream().map(GdnDtlGoodsSimpleReq::getSnQty).reduce((x, y) -> x.add(y)).get();
            //写库存交易凭证，出库凭证(T03)，源单号是出库单 调服务
            int line = 1;
            insertTrsn(singleGroupSn, loginName, gdnNo, line, gdnDtl.getGdnLineNo(), gorupQty, false);
            line++;
        }
        //(4)更新 装柜单(WMS_CTNR_LOADING)的
        //   状态(STATUS) =进行中(2);
        //   实际装柜数量(CMP_QTY) +=本次装运数量。
        ctnrLoadingMapper.updateStatusAndQtyByNo(CtnrLoadingStatusEnum.DOING.code, thisQty, ctnrNo, gdnDtlId);

        simpleReqs.stream().forEach(o -> {
            //（5） 插入全部SN到 装柜单货品清单(WMS_CTNR_LOADING_GOODS) 表
            Goods goods = goodsMapper.selectOne(new QueryWrapper<>(new Goods().setGoodsSn(o.getSn())));
            if (goods == null) {
                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无对应条码");
            }
            CtnrLoading loading = ctnrLoadingMapper.selectOne(new QueryWrapper<>(new CtnrLoading().setGdnDtlId(gdnDtlId).setCntrNo(ctnrNo)));
            if (loading == null) {
                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无对应装柜单");
            }
            CtnrLoadingGoods ctnrLoadingGoods = new CtnrLoadingGoods();
            ctnrLoadingGoods.setComments("发货装运");
            ctnrLoadingGoods.setCtnrLoadingId(loading.getCtnrLoadingId());
            ctnrLoadingGoods.setGoodsId(goods.getGoodsId());
            ctnrLoadingGoods.setQty(goods.getQty());
            ctnrLoadingGoods.setStatus(goods.getStatus());
            ctnrLoadingGoods.setGoodsSn(goods.getGoodsSn());
            ctnrLoadingGoods.setCreateTime(new Date());
            ctnrLoadingGoods.setCreateUser(loginName);
            ctnrLoadingGoodsMapper.insert(ctnrLoadingGoods);

            // (6) 更新 发货单明细表(WMS_GDN_DTL)，若有跨柜装运，则根据SN找到对应的发货单明细更新。
            //状态(STATUS) =装柜中(6)
            //发货数量(SHIP_QTY) +=本次装运数量。
            //***** 2021/04/28 ***** 跨柜装时发货数量操作哪个明细行就加哪个明细行，但是货品记录还是记录到原来的发货单明细
//            gdnDtlMapper.updateStatusAndQty(GdnDtlStatusEnum.LOADING.getCode(), o.getSnQty(), gdnDtlId);
            gdnDtlMapper.updateStatusQty(GdnDtlStatusEnum.LOADING.getCode(), o.getSnQty(), gdnDtlId,loginName,new Date());

            //(7) 插入全部SN到发货通知单明细货品清单(WMS_GDN_DTL_GOODS) 表，若有跨柜装运，则根据SN找到对应的发货单明细插入清单。
            GdnDtlGoods gdnDtlGoods = new GdnDtlGoods();
            gdnDtlGoods.setComments("发货装运");
            gdnDtlGoods.setGdnDtlId(gdnDtlId);   //***** 2021/04/29 luxm ***** 跨柜装时发货数量操作哪个明细行就加哪个明细行，货品记录也记录到原来的发货单明细
            gdnDtlGoods.setGoodsId(goods.getGoodsId());
            gdnDtlGoods.setGoodsSn(goods.getGoodsSn());
            gdnDtlGoods.setQaStatus(goods.getQaStatus());
            gdnDtlGoods.setQty(goods.getQty());
            gdnDtlGoods.setCreateTime(new Date());
            gdnDtlGoods.setCreateUser(loginName);
            gdnDtlGoods.setStatus(goods.getStatus());
            gdnDtlGoodsMapper.insert(gdnDtlGoods);
        });

//        List<Goods> goods = goodsMapper.selectBySNs(sns);

        ArrayList<Integer> ids = new ArrayList<>();
        for (String sn : sns) {
            Goods goods = goodsMapper.selectOne(new QueryWrapper<>(new Goods().setGoodsSn(sn)));
//
//        List<Integer> ids = goods.stream().map(Goods::getGoodsId).collect(Collectors.toList());
//        //移到历史表
//        for (Goods good : goods) {
            GoodsHis goodsHis = new GoodsHis();
            BeanUtil.copyNonNull(goods, goodsHis);
            goodsHis.setLogCreateTime(new Date());
            goodsHis.setLogCreateUser(loginName);
            goodsHisMapper.insert(goodsHis);
            ids.add(goods.getGoodsId());
        }
        //删除
        for(Integer id :ids){
            goodsMapper.deleteById(id);
        }
        //int i = goodsMapper.deleteBatchIds(ids);

        //⑧	更新 发货单(WMS_GDN_HEADER)的
        //        状态(STATUS) = 进行中(3)
        set.stream().forEach(o -> {
            gdnHeaderMapper.updateStatusByNo(GdnHeaderStatusEnum.DOING.code, o);
        });

        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;

        //记录GOODS操作日志
        List<Goods> goodsChangeList = goodsMapper.selectBySNs(snOriginalList);

        Map<String, List<Goods>> mapGoods = goodsChangeList.stream().collect(Collectors.groupingBy(s -> s.getGoodsSn()));

        for (Goods goods : goodsOriginalList) {
//            if (mapGoods.containsKey(goods.getGoodsSn())) {
            String jsonOriginalString = JSON.toJSONString(goods);



            String jsonChangeString = "发货装运已删除";


            GoodsLog goodsLog = new GoodsLog();
            goodsLog.setOpCode(GoodsLogEnum.SHIP.getCode());
            goodsLog.setReason(GoodsLogEnum.SHIP.getEnumName());

            goodsLog.setGoodsSn(goods.getGoodsSn());
            goodsLog.setOperator(loginName);
            goodsLog.setCreateUser(loginName);
            goodsLog.setCreateTime(new Date());


            String updateContent = new String();
            updateContent = "未更新的:" + jsonOriginalString + ",更新后的:" + jsonChangeString;
            goodsLog.setUpdateContent(updateContent);

            String comments = String.format("操作人【%s】,发货单明细Id:【%s】", loginName, gdnCtnrCommitRequest.getGdnDtlId());
            goodsLog.setComments(comments);

            int insertRow = goodsLogMapper.insert(goodsLog);
            log.info("成功新增{}条记录", insertRow);
//            }
        }
        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;
        //提交后删除缓存
        deleteSnCacheList(gdnDtlId, gdnHeader.getGdnNo(), null);
        //提交后删除缓存


        return new Result<>(true, "提交成功");
    }


    /**
     * ②	写库存交易凭证，出库凭证(T03)，源单号是出库单 调服务
     *
     * @param singleGroupSn
     * @param loginName
     * @param gdnNo
     * @param line
     * @param gorupQty
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void insertTrsn(List<String> singleGroupSn, String loginName, String gdnNo,
                           Integer line, Integer docLine, BigDecimal gorupQty, Boolean isRemain) throws Exception {
        TrsnDto trsnDto = new TrsnDto();

        try {
            //生成凭证号
            String noteNo = this.basicSimpleCodeRuleApi.GenerateCode(this.NOTE_NO);
            Goods goods = goodsMapper.selectBySn(singleGroupSn.get(0));
//            TODO 测试代码，测试和投产记得删除
//            int a = 0 / 0;

            log.warn("-------------------------->凭证号，出库 noteNo :{}", noteNo);
            //表头
            TrsnHeader trsnHeader = new TrsnHeader();
            trsnHeader.setNoteNo(noteNo);
            trsnHeader.setNoteType("T03");
            trsnHeader.setWhsId(goods.getWhsId());
            trsnHeader.setFactory(goods.getFactory());
            trsnHeader.setVendorId(goods.getVendorId());
            trsnHeader.setFmDoc("/");
            trsnHeader.setDocRef1("/");
            trsnHeader.setDocRef2("/");
            trsnHeader.setDocRef3("/");
            trsnHeader.setDocRef4("/");
            trsnHeader.setDocRef5("/");
            trsnHeader.setComments("/");
            trsnHeader.setStatus(TrsnHeaderStatusEnum.CMP.code);
            trsnHeader.setCreateUser(loginName);
            trsnHeader.setCreateTime(new Date());
            trsnHeader.setOwner(goods.getOwner());
            trsnHeader.setFmDoc(gdnNo);
            //            明细
            trsnDto.setTrsnHeader(trsnHeader);
            ArrayList<TrsnDtlDto> trsnDtlDtos = new ArrayList<>();
            TrsnDtlDto trsnDtlDto = new TrsnDtlDto();
            TrsnDtl trsnDtl = new TrsnDtl();
            trsnDtl.setNoteNo(noteNo);
            trsnDtl.setTrsnHeaderId(trsnHeader.getTrsnHeaderId());
            trsnDtl.setQaStatus(goods.getQaStatus());
            trsnDtl.setFmDoc(gdnNo);
            trsnDtl.setFmDocLine(String.valueOf(docLine));
            trsnDtl.setInvUpdateFlag(-1);
            trsnDtl.setUnit(goods.getUnit());
            trsnDtl.setNoteLineNo(String.valueOf(line));
            trsnDtl.setLotAttr1("/");
            trsnDtl.setLotAttr2("/");
            trsnDtl.setLotAttr3("/");
            trsnDtl.setLotAttr4("/");
            trsnDtl.setLotAttr5("/");
            trsnDtl.setLotAttr6("/");
            trsnDtl.setLotAttr7("/");
            trsnDtl.setLotAttr8("");
            trsnDtl.setLotAttr9("/");
            trsnDtl.setLotAttr10("/");
            trsnDtl.setLotAttr11("/");
            trsnDtl.setLotAttr12("/");
            trsnDtl.setQty(gorupQty);
            trsnDtl.setInvType("");
            trsnDtl.setStatus(TrsnDtlStatusEnum.CMP.code);
            trsnDtl.setComments("/");
            trsnDtl.setSkuId(goods.getSkuId());
            trsnDtl.setCreateTime(new Date());
            trsnDtl.setCreateUser(loginName);
            //明细-goods
            ArrayList<TrsnDtlGoods> dtlGoodsArrayList = new ArrayList<>();
            if (!CollectionUtil.isEmpty(singleGroupSn)) {
                for (String sn : singleGroupSn) {
//                    GdnDtlGoods dtlGoods = gdnDtlGoodsMapper.selectOne(new QueryWrapper<>(new GdnDtlGoods().setGoodsSn(sn)));
                    Goods g = goodsMapper.selectBySn(sn);
                    TrsnDtlGoods trsnDtlGoods = new TrsnDtlGoods();
                    trsnDtlGoods.setTrsnDtlId(trsnDtl.getTrsnDtlId());
                    trsnDtlGoods.setQaStatus(g.getQaStatus());
                    trsnDtlGoods.setGoodsId(g.getGoodsId());
                    trsnDtlGoods.setGoodsSn(g.getGoodsSn());
                    trsnDtlGoods.setQty(g.getQty());
                    trsnDtlGoods.setComments("发货装运出库");
                    trsnDtlGoods.setStatus(TrsnDtlGoodsStatusEnum.RECEIVED.getCode());
                    trsnDtlGoods.setCreateTime(new Date());
                    trsnDtlGoods.setCreateUser(loginName);
                    dtlGoodsArrayList.add(trsnDtlGoods);
                    //变更库位库存
                    updateLocInventory(g, gdnNo, noteNo, loginName, line, isRemain);
                }
                trsnDtlDto.setTrsnDtl(trsnDtl);
                trsnDtlDto.setTrsnDtlGoodsList(dtlGoodsArrayList);
                trsnDtlDtos.add(trsnDtlDto);
                trsnDto.setTrsnDtlDtoList(trsnDtlDtos);
                trsnService.addNewOne(trsnDto);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("-------------->发货装运:写库存交易凭证失败");
            throw e;
        }
    }


    /**
     * 变更库位库存
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void updateLocInventory(Goods goods, String gdnNo, String noteNo, String loginName, int line, Boolean isRemain) throws Exception {
        UpdateInventoryInputDto dto = new UpdateInventoryInputDto();

//            GdnDtlGoods gdnDtlGoods = gdnDtlGoodsMapper.selectOne(new QueryWrapper<>(new GdnDtlGoods().setGoodsSn(snAndQty.getGoodsSN())));
        try {
            dto.setLocId(goods.getLocId());//设置库位
            if (isRemain) {
                dto.setQty(BigDecimal.ZERO);
            } else {
                dto.setQty(goods.getQty().negate());
            }
            dto.setPreInQty(BigDecimal.ZERO);
            dto.setPreOutQty(BigDecimal.ZERO);
            dto.setHoldQty(BigDecimal.ZERO);
            dto.setAlocQty(goods.getQty().negate());

            dto.setPreAlocQty(BigDecimal.ZERO);
            dto.setOperator(loginName);
            dto.setWhsId(goods.getWhsId());
            dto.setOwner(goods.getOwner());
            dto.setSkuId(goods.getSkuId());
            dto.setFactory(goods.getFactory());
            dto.setVendorId(goods.getVendorId());
            dto.setLotNo(goods.getLotno());
            dto.setInvAttr(goods.getInvAttr());
            dto.setQaStatus(goods.getQaStatus());
            dto.setUnit(goods.getUnit());
            //设置库区
            LocationResponse location = locationService.getLocation(goods.getLocId());
            if (location!=null) {
                dto.setZoneId(location.getZoneId());
            }

            InvTrans log = new InvTrans();
            log.setOpCode(OpCodeEnum.CONTAINERLOAD.getDescription());
            log.setDocType("出库单");
            log.setDocNo(gdnNo);
            log.setDocLineNo(String.valueOf(line));
            log.setTrsnNo(noteNo);
            log.setTrsnLineNo(String.valueOf(" "));
            log.setStatus(InvTransStatusEnum.FINISHED.getCode());
            log.setTransTime(new Date());
            log.setOperator("system");
            log.setTransQty(goods.getQty());
            log.setCreateTime(new Date());
            log.setCreateUser("system");
            inventoryService.inventoryOut(dto, log);
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("变更库位库存失败");
            log.error(e.toString());
            log.error(e.getMessage());
            throw e;
        }
    }

    /**
     * 结束装柜
     *
     * @param gdnCtnrCommitRequest
     * @param loginName
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<?> finish(GdnCtnrCommitRequest gdnCtnrCommitRequest, String loginName) throws Exception {
        String ctnrNo = gdnCtnrCommitRequest.getCtnrNo(); //实际柜号
        Long gdnDtlId = gdnCtnrCommitRequest.getGdnDtlId(); //明细ID
        List<SnAndQtyRequest> snList = gdnCtnrCommitRequest.getSnList(); //条码列表
        BigDecimal thisQty = BigDecimal.ZERO;

        CtnrLoading status = ctnrLoadingMapper.selectOne(new QueryWrapper<>(new CtnrLoading().setCntrNo(ctnrNo).setGdnDtlId(gdnDtlId)));
        if (status == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无对应装柜单");
        }
        if (status.getStatus() == 3) {
            Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "装柜单已完成");
        }


        // 7)	若本次装运数量，仍然有未提交的，先执行5）【提交】按钮的方法，再执行如下操作
        if (!CollectionUtil.isEmpty(snList)) {
            thisQty = snList.stream().map(SnAndQtyRequest::getQty).reduce((x, y) -> x.add(y)).get();//这次提交的总数量
            this.commit(gdnCtnrCommitRequest, loginName);
        }
        /**
         * ①	更新 装柜单(WMS_CTNR_LOADING)的
         * 状态(STATUS) =完成(3);
         * ②	更新 发货单明细表(WMS_GDN_DTL)的
         * 状态(STATUS) =发货单明细的装柜单状态全都是完成(3) ? 已发运(7) : 装柜中(6)
         */
//        ctnrLoadingMapper.updateStatusAndQtyByNo(CtnrLoadingStatusEnum.FINISH.code, BigDecimal.ZERO, ctnrNo, gdnDtlId);
        ctnrLoadingMapper.updateStatusQtyModifyByNo(CtnrLoadingStatusEnum.FINISH.code, BigDecimal.ZERO, ctnrNo, gdnDtlId,loginName,new Date());

        List<String> ctnrNoList = ctnrLoadingMapper.selectCtnrNoListByDtlId(gdnDtlId);
        HashSet<Integer> statuss = new HashSet<>();
        for (String no : ctnrNoList) {
            CtnrLoading ctnrLoading = ctnrLoadingMapper.selectOne(new QueryWrapper<>(new CtnrLoading().setCntrNo(no).setGdnDtlId(gdnDtlId)));
            statuss.add((ctnrLoading.getStatus()));
        }
        if (statuss.size() == 1 && statuss.contains(CtnrLoadingStatusEnum.FINISH.code)) {
//            gdnDtlMapper.updateStatusAndQty(GdnDtlStatusEnum.SHIPMENTED.code, BigDecimal.ZERO, gdnDtlId);
            gdnDtlMapper.updateStatusQty(GdnDtlStatusEnum.SHIPMENTED.code, BigDecimal.ZERO, gdnDtlId,loginName,new Date());
        } else {
//            gdnDtlMapper.updateStatusAndQty(GdnDtlStatusEnum.LOADING.code, BigDecimal.ZERO, gdnDtlId);
            gdnDtlMapper.updateStatusQty(GdnDtlStatusEnum.LOADING.code, BigDecimal.ZERO, gdnDtlId,loginName,new Date());
        }
        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;
        return new Result<>(true, "成功");
    }

    /**
     * 检查是否满足关单条件
     *
     * @param gdnNo
     * @return
     */
    public Result<GdnCtnrTaskResponse> checkIsClose(String gdnNo) {
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<>(new GdnHeader().setGdnNo(gdnNo)));
        if (gdnHeader == null) {
            return new Result<>(false, "查无此发货单");
        }
        if (GdnHeaderStatusEnum.DOING.code != gdnHeader.getStatus()) {
            return new Result<>(false, "发货单不是[进行中]状态");
        }

        //取消状态=7的限制
//        List<GdnDtl> gdnDtls = gdnDtlMapper.selectList(new QueryWrapper<>(new GdnDtl().setGdnHeaderId(gdnHeader.getGdnHeaderId())));
//        if (!CollectionUtil.isEmpty(gdnDtls)) {
//            List<Integer> collect = gdnDtls.stream().map(GdnDtl::getStatus).distinct().collect(Collectors.toList());
//            if (collect.size() != 1 || GdnDtlStatusEnum.SHIPMENTED.code != collect.get(0)) {
//                return new Result<>(false, "发货单明细没有全部进入[已发运]状态");
//            }
//        }
        //如果满足关单条件，检查是否余留单
        /**
         * 本单需求：发货单明细(WMS_GDN_DTL)的订单数(ORDER_QTY)汇总
         * 本单已装：发货单明细(WMS_GDN_DTL)的发货数量(SHIP_QTY) 汇总
         * 实际已装：发货单对应的装柜单(WMS_CTNR_LOADING)的实际装柜数量(CMP_QTY)汇总
         */
        GdnCtnrRequest gdnCtnrRequest = new GdnCtnrRequest();
        gdnCtnrRequest.setGdnNo(gdnNo);

        List<GdnCtnrTaskResponse> gdnCtnrTaskResponses = gdnHeaderMapper.selectGndCtnrList(gdnCtnrRequest);
        GdnCtnrTaskResponse g = gdnCtnrTaskResponses.get(0);
        GdnCtnrTaskResponse gdnCtnrTaskResponse = new GdnCtnrTaskResponse();

        //发货单需求数量总和
        BigDecimal orderQtySum = BigDecimal.ZERO;
        //发货单已捡数量总和
        BigDecimal pickQtySum = BigDecimal.ZERO;
        //发货单已装数量总和
        BigDecimal cmpQtySum = BigDecimal.ZERO;
        //发货单已装数量总和（主页是取明细的ShipQty汇总）
        BigDecimal shipQtySum = BigDecimal.ZERO;

        //按照明细分组
        Map<Integer, List<GdnCtnrTaskResponse>> groupDtl = gdnCtnrTaskResponses.stream().collect(Collectors.groupingBy(GdnCtnrTaskResponse::getGdnDtlId));
        Set<Integer> dtlKeys = groupDtl.keySet();
        for (Iterator<Integer> dtlKey = dtlKeys.iterator(); dtlKey.hasNext(); ) {
            List<GdnCtnrTaskResponse> gdnCtnrTaskResponses1 = groupDtl.get(dtlKey.next());
            if (gdnCtnrTaskResponses1.get(0).getOrderQtySum() != null) {

                orderQtySum = orderQtySum.add(gdnCtnrTaskResponses1.get(0).getOrderQtySum());

                //加NULL判断
                BigDecimal qtySum = gdnCtnrTaskResponses1.get(0).getPickQtySum();
                if (qtySum == null) {
                    qtySum = new BigDecimal(0);
                }
                pickQtySum = pickQtySum.add(qtySum);


                //加NULL判断
                BigDecimal qtySum1 = gdnCtnrTaskResponses1.get(0).getShipQtySum();
                if (qtySum1 == null) {
                    qtySum1 = new BigDecimal(0);
                }
                shipQtySum = shipQtySum.add(qtySum1);
            }
            List<GdnCtnrTaskResponse> c3 = gdnCtnrTaskResponses.stream().filter(c -> c.getCmpQtySum() != null && c.getGdnDtlId().equals(gdnCtnrTaskResponses1.get(0).getGdnDtlId())).collect(Collectors.toList());
            if (!CollectionUtil.isEmpty(c3)) {
                cmpQtySum = cmpQtySum.add(c3.stream().map(GdnCtnrTaskResponse::getCmpQtySum).reduce((x, y) -> x.add(y)).get());

            }
        }

        gdnCtnrTaskResponse.setCmpQtySum(cmpQtySum);
        gdnCtnrTaskResponse.setShipQtySum(shipQtySum);
        gdnCtnrTaskResponse.setOrderQtySum(orderQtySum);
        gdnCtnrTaskResponse.setGdnNo(g.getGdnNo());
        gdnCtnrTaskResponse.setRefCntrNo(g.getRefCntrNo());
        gdnCtnrTaskResponse.setComments(g.getComments());
        gdnCtnrTaskResponse.setRemainPdaList(gdnDtlMapper.selectAllAndUnshipQtyBySourceNo(gdnHeader.getSourceShipNotice()));
        /*if (g.getOrderQtySum().compareTo(g.getShipQtySum()) == 1) {
            //余留信息
            List<GdnCtnrDtlResponse> remainInfoList = new ArrayList<>();

            List<Integer> dtlIds = gdnCtnrTaskResponses.stream().map(GdnCtnrTaskResponse::getGdnDtlId).distinct().collect(Collectors.toList());

            for (Integer dtlId : dtlIds) {
                List<GdnCtnrDtlResponse> gdnCtnrDtlResponses = gdnDtlMapper.selectRemainInfo(dtlId);
                if (CollectionUtil.isEmpty(gdnCtnrDtlResponses)) {
                    continue;
                }
                GdnCtnrDtlResponse gdnCtnrDtlResponse = gdnCtnrDtlResponses.get(0);
                Warehouse warehouse = this.queryWhs(gdnCtnrDtlResponse.getWhsId());
                gdnCtnrDtlResponse.setWhsName(warehouse == null ? null : warehouse.getWhsName());
                gdnCtnrDtlResponse.setWhsCode(warehouse == null ? null : warehouse.getWhsCode());
                if (gdnCtnrDtlResponse.getOrderQty().compareTo(gdnCtnrDtlResponse.getShipQty()) == 1) {
                    gdnCtnrDtlResponse.setRemainQty(gdnCtnrDtlResponse.getOrderQty().subtract(gdnCtnrDtlResponse.getShipQty()));
                    remainInfoList.add(gdnCtnrDtlResponse);
                }
            }
            gdnCtnrTaskResponse.setRemainInfoList(remainInfoList);
        }*/
        return new Result<>(true, "", gdnCtnrTaskResponse);
    }

    /**
     * 结束装运并关单
     *
     * @param gdnNo
     * @param isCreateRemainNote
     * @param loginName
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<?> close(String gdnNo, Integer isCreateRemainNote, String loginName) {
        //WMS发货回写SAP
        try {
            writeToAsp(gdnNo);
        } catch (Exception e) {
            e.printStackTrace();
            Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "【发货装运】回写SAP系统失败");
        }
        //货柜装货完成后，出库数量和装柜清单回写OMS
        try {
            writeToOms(gdnNo);
        } catch (Exception e) {
            e.printStackTrace();
            Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "【发货装运】回写OMS系统失败");
        }
        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;

        //TODO:后续发布

        try {
            wriiteToTms(gdnNo);
        }catch (Exception e){
            Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "【发货装运】回写TMS系统失败");
        }
//        Shift.fatal("///////////////");
        gdnHeaderMapper.updateStatusByNo(GdnHeaderStatusEnum.FINISH.code, gdnNo);
//        gdnDtlMapper.updateStatusByGdnNo(GdnDtlStatusEnum.SHIPMENTED.code, gdnNo);
        gdnDtlMapper.updateCloseStatusByGdnNo(GdnDtlStatusEnum.SHIPMENTED.code, gdnNo,loginName,new Date());
        return new Result<>(true, "成功");
    }


    /**
     * WMS发货回写SAP
     *
     * @param gdnNo
     * @throws Exception
     */
    public void writeToAsp(String gdnNo) throws Exception {
        IfPostback ifPostback = new IfPostback();
        ifPostback.setDestSystem(OtherSystemEnum.SYSYTEM_SAP.getEnumName());
        ifPostback.setPostbackTopic(TopicEnum.TOPIC_SAP_DELIVERY.getEnumName());
        ifPostback.setCreateTime(new Date());
        ifPostback.setPostbackTime(new Date());
        ifPostback.setPostbackResult(BackResultEnum.DEFAULT_STATE.code);
        ifPostback.setPostbackNo(gdnNo);
        ifPostback.setPostbackSourceNo("");
        ifPostback.setPostbackType(GdnTypeEnum.D01.code);

        String gdnDataWithSap = this.getGdnDataWithSap(gdnNo);
        ifPostback.setPostbackContent(gdnDataWithSap);
        ifPostbackMapper.insert(ifPostback);
    }


    /**
     * 货柜装货完成后，出库数量和装柜清单回写OMS
     *
     * @param gdnNo
     */
    public void writeToOms(String gdnNo) throws Exception {
        IfPostback ifPostback = new IfPostback();
        ifPostback.setDestSystem(OtherSystemEnum.SYSYTEM_OME.getEnumName());
        ifPostback.setPostbackTopic(TopicEnum.TOPIC_OMS_ACTUAL_CTNR.getEnumName());
        ifPostback.setPostbackResult(BackResultEnum.DEFAULT_STATE.code);
        ifPostback.setCreateTime(new Date());
//        ifPostback.setPostbackTime(new Date());
        ifPostback.setPostbackNo(gdnNo);
        ifPostback.setPostbackSourceNo("");
        ifPostback.setPostbackType(GdnTypeEnum.D01.code);

        String gdnDataWithOms = this.getGdnDataWithOms(gdnNo);
        ifPostback.setPostbackContent(gdnDataWithOms);
        ifPostbackMapper.insert(ifPostback);
    }


    /**
     * 根据WHS_ID查名称和code
     */
    public Warehouse queryWhs(Integer whsId) {
        Warehouse warehouse = warehouseMapper.selectById(whsId);
        return warehouse;
    }

    /**
     * 获取SAP接口发货单报文Json字符串
     *
     * @param gdnNo 发货单号
     * @return
     */
    public String getGdnDataWithSap(String gdnNo) {
        List<GdnOmsInfoDto> gdnOmsInfoDtos = gdnHeaderMapper.selectOmsInfo(gdnNo);
        if (CollectionUtil.isEmpty(gdnOmsInfoDtos)) {
            return null;
        }

        GdnOmsInfoDto gdnOmsInfoDto = gdnOmsInfoDtos.get(0);
        ZDT_003_ReqRecordsHead zdt_003_reqRecordsHead = new ZDT_003_ReqRecordsHead();
        zdt_003_reqRecordsHead.setZWJHD(gdnOmsInfoDto.getGdnNo() == null ? "" : gdnOmsInfoDto.getGdnNo()); //单号
        zdt_003_reqRecordsHead.setVSTEL(""); //传空
        zdt_003_reqRecordsHead.setWADAT_IST(gdnOmsInfoDto.getGdnApprovedTime() == null ? "" : gdnOmsInfoDto.getGdnApprovedTime().toString()); //审核日期/生效日期;
        ZDT_Reserve reserve = new ZDT_Reserve();
        reserve.setRsv1("");
        reserve.setRsv2("");
        reserve.setRsv3("");
        reserve.setRsv4("");
        reserve.setRsv5("");
        zdt_003_reqRecordsHead.setRSV(reserve);

        ZDT_003_ReqRecordsHeadItems[] arr = new ZDT_003_ReqRecordsHeadItems[gdnOmsInfoDtos.size()];

        int i = 0;
        for (GdnOmsInfoDto omsInfoDto : gdnOmsInfoDtos) {
            ZDT_003_ReqRecordsHeadItems items = new ZDT_003_ReqRecordsHeadItems();

            items.setZWFHH(omsInfoDto.getGdnLineNo() == null ? "" : omsInfoDto.getGdnLineNo().toString());  //行号

            BigDecimal planQty = omsInfoDto.getPlanQty();
            if (planQty == null) {
                planQty = BigDecimal.ZERO;
            }
            items.setLFIMG(omsInfoDto.getShipQty() == null ? "" : planQty.toString());  //数量

            items.setLGORT("");  //库存地点
            if (omsInfoDto.getFactory() != null && omsInfoDto.getWhsCode() != null) {
                CodeMapping codeMapping = codeMappingNewService.selectByWhsCodeAndFactory(omsInfoDto.getFactory(), omsInfoDto.getWhsCode(), omsInfoDto.getQaStatus());
                if (codeMapping != null) {
                    items.setLGORT(codeMapping.getMappingCode());  //库存地点
                }
            }
            items.setMATNR(omsInfoDto.getSkuCode() == null ? "" : omsInfoDto.getSkuCode());  //物料编码
            items.setVGBEL(omsInfoDto.getSoNo() == null ? "" : omsInfoDto.getSoNo());  //销售订单
            items.setVGPOS(omsInfoDto.getSoLineNo() == null ? "" : omsInfoDto.getSoLineNo().toString());  //销售订单行号


            BigDecimal cmpQty = omsInfoDto.getCmpQty();
            if (cmpQty == null) {
                cmpQty = BigDecimal.ZERO;
            }
            items.setPIKMG(omsInfoDto.getShipQty() == null ? "" : cmpQty.toString());  //数量

            ZDT_Reserve reserve1 = new ZDT_Reserve();
            ZDT_003_ReqRecordsHeadItemsRSV_A rsvA = new ZDT_003_ReqRecordsHeadItemsRSV_A();
            ZDT_003_ReqRecordsHeadItemsRSV_B rsvB = new ZDT_003_ReqRecordsHeadItemsRSV_B();
            reserve1.setRsv1("");
            reserve1.setRsv2("");
            reserve1.setRsv3(omsInfoDto.getSourceShipNotice() == null ? "" : omsInfoDto.getSourceShipNotice());
            reserve1.setRsv4(omsInfoDto.getSourceShipNoticeItem() == null ? "0" : omsInfoDto.getSourceShipNoticeItem().toString());
            //新单号不为空 才传旧单号

            if(StringUtils.isNotBlank(omsInfoDto.getRefSoNo())&&StringUtils.isNotBlank(omsInfoDto.getRefSoLineNo())){
                reserve1.setRsv5(omsInfoDto.getSoNo()+"");
                rsvA.setRsv1(omsInfoDto.getSoLineNo()+"");
                items.setVGBEL(omsInfoDto.getRefSoNo() +"");  //销售订单
                items.setVGPOS(omsInfoDto.getRefSoLineNo());
            }else {
                items.setVGBEL(omsInfoDto.getSoNo() == null ? "" : omsInfoDto.getSoNo());  //销售订单
                items.setVGPOS(omsInfoDto.getSoLineNo() == null ? "" : omsInfoDto.getSoLineNo().toString());  //销售订单行号

            }

            items.setRSV(reserve1);
            items.setRSV_A(rsvA);
            items.setRSV_B(rsvB);
            arr[i] = items;
            i++;
        }

        zdt_003_reqRecordsHead.setItems(arr);

        ArrayList<ZDT_003_ReqRecordsHead> list = new ArrayList<>();
        list.add(zdt_003_reqRecordsHead);
        return JSON.toJSONString(list);
    }

    /**
     * 获取OMS接口发货单报文Json字符串
     *
     * @param gdnNo
     * @return
     */
    public String getGdnDataWithOms(String gdnNo) {
        List<GdnOmsInfoDto> gdnOmsInfoDtos = gdnHeaderMapper.selectOmsInfo(gdnNo);
        if (CollectionUtil.isEmpty(gdnOmsInfoDtos)) {
            return null;
        }

        //处理CntrNo 为NULL不能作为KEY分组
        for (GdnOmsInfoDto gdnOmsInfoDto : gdnOmsInfoDtos) {
            if (gdnOmsInfoDto.getCntrNo() == null) {
                gdnOmsInfoDto.setCntrNo("");
            }
        }

        Map<String, List<GdnOmsInfoDto>> collect = gdnOmsInfoDtos.stream().collect(Collectors.groupingBy(GdnOmsInfoDto::getCntrNo));
        Set<String> ctnrNos = collect.keySet();
        int ctnrSeq = 1;
        HashMap<String, Integer> ctnrAndSeq = new HashMap<>();//柜号和序号
        for (String ctnrNo : ctnrNos) {
            ctnrAndSeq.put(ctnrNo, ctnrSeq++);
        }

        ArrayList<Req_Pl> req_pls = new ArrayList<>();


        for (GdnOmsInfoDto gdnOmsInfoDto : gdnOmsInfoDtos) {
            Req_Pl req_pl = new Req_Pl();
            req_pl.setOms_Notice_No(gdnOmsInfoDto.getSourceShipNotice() == null ? "" : gdnOmsInfoDto.getSourceShipNotice()); //OMS发货通知单号
            String cntrNo = gdnOmsInfoDto.getCntrNo();
//            if (cntrNo == null) {
            //上面这是原来的
            if (cntrNo == null || cntrNo.equals("")) {
                req_pl.setCabno(""); //柜号
//                req_pl.setSer(1); //货柜序号
            } else {
                req_pl.setCabno(cntrNo);
//                req_pl.setSer(ctnrAndSeq.get(cntrNo));
            }
            //货柜序号
            if(gdnOmsInfoDto.getCntrSeq() == null){
                req_pl.setSer(1);
            }else{
                req_pl.setSer(gdnOmsInfoDto.getCntrSeq());
            }
            req_pl.setWms_Flag(gdnOmsInfoDto.getSourceShipNotice() + gdnOmsInfoDto.getId()+gdnOmsInfoDto.getCtnrId()); //WMS唯一标识
            req_pl.setOms_Notice_Item(gdnOmsInfoDto.getSourceShipNoticeItem() == null ? 0 : Integer.parseInt(gdnOmsInfoDto.getSourceShipNoticeItem())); //OMS发货通知单行项目
            req_pl.setSap_So_No(gdnOmsInfoDto.getSoNo() == null ? "" : gdnOmsInfoDto.getSoNo()); //SAP销售订单号
            req_pl.setSap_So_Item(gdnOmsInfoDto.getSoLineNo() == null ? 0 : gdnOmsInfoDto.getSoLineNo()); //SAP销售订单行项目号
            req_pl.setItem_Code(gdnOmsInfoDto.getSkuCode() == null ? "" : gdnOmsInfoDto.getSkuCode()); //物料编码
            req_pl.setCabtype(gdnOmsInfoDto.getCntrType() == null ? "" : gdnOmsInfoDto.getCntrType()); //柜型
            req_pl.setSealno(gdnOmsInfoDto.getSealNo() == null ? "" : gdnOmsInfoDto.getSealNo()); //封条号
            req_pl.setCarno(gdnOmsInfoDto.getVehicleNo() == null ? "" : gdnOmsInfoDto.getVehicleNo()); //车牌号
            req_pl.setCarbinno(gdnOmsInfoDto.getBookingNo() == null ? "" : gdnOmsInfoDto.getBookingNo()); //订舱号
            req_pl.setFactqty(gdnOmsInfoDto.getCmpQty() == null ? BigDecimal.ZERO : gdnOmsInfoDto.getCmpQty());  //实际装柜数量
            req_pl.setQty(gdnOmsInfoDto.getPlanQty() == null ? BigDecimal.ZERO : gdnOmsInfoDto.getPlanQty()); //计划装柜数量
            req_pl.setNote(gdnOmsInfoDto.getComments() == null ? "" : gdnOmsInfoDto.getComments()); //备注
            req_pl.setAttribute1(gdnOmsInfoDto.getNetWeight() == null ? "" : gdnOmsInfoDto.getNetWeight().toString()); //净重
            req_pl.setAttribute2(gdnOmsInfoDto.getGrossWeight() == null ? "" : gdnOmsInfoDto.getGrossWeight().toString()); //毛重
            req_pl.setAttribute3(gdnOmsInfoDto.getLength() == null ? "" : gdnOmsInfoDto.getLength().toString()); //长
            req_pl.setAttribute4(gdnOmsInfoDto.getWidth() == null ? "" : gdnOmsInfoDto.getWidth().toString()); //宽
            req_pl.setAttribute5(gdnOmsInfoDto.getHeight() == null ? "" : gdnOmsInfoDto.getHeight().toString()); //高
            req_pl.setAttribute6(gdnOmsInfoDto.getPoNo());//订单号
            req_pl.setAttribute7(gdnOmsInfoDto.getDescCn());//物料编码

            req_pl.setAttribute8("");
            req_pl.setAttribute9("");
            req_pl.setAttribute10("");
            req_pls.add(req_pl);
        }
        return JSON.toJSONString(req_pls);
    }

    /**
     * 结束关单前校验查询
     *
     * @param gdnNo
     * @param loginName
     * @return
     */
    public Result<?> closeBeforeSelect(String gdnNo, String loginName) {

        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<>(new GdnHeader().setGdnNo(gdnNo)));
        if (gdnHeader == null) {
//            return new Result<>(false, "查无此发货单");
            Shift.fatal(StatusCode.BUSINESS_ERR, "查无此发货单");
        }
        if (GdnHeaderStatusEnum.DOING.code != gdnHeader.getStatus()) {
//            return new Result<>(false, "发货单不是[进行中]状态");
            Shift.fatal(StatusCode.BUSINESS_ERR, "发货单不是[进行中]状态");
        }

        List<GdnDtl> gdnDtls = gdnDtlMapper.selectList(new QueryWrapper<>(new GdnDtl().setGdnHeaderId(gdnHeader.getGdnHeaderId())));
        if (!CollectionUtil.isEmpty(gdnDtls)) {
//            List<Integer> collect = gdnDtls.stream().map(GdnDtl::getStatus).distinct().collect(Collectors.toList());
//            if (collect.size() != 1 || GdnDtlStatusEnum.SHIPMENTED.code != collect.get(0)) {
//                return new Result<>(false, "发货单明细没有全部进入[已发运]状态");
//            }
            int shipmentedCode = GdnDtlStatusEnum.SHIPMENTED.getCode();//已发运
            //查询不为【已发运】DE
            List<Integer> collect = gdnDtls.stream().map(GdnDtl::getStatus).filter(s -> !(s.equals(shipmentedCode))).collect(Collectors.toList());
            if (collect.size() > 0) {
                String message = String.format("还有[%s]笔装运明细为未发运状态，确认要结束装运并关单？", collect.size());
                return new Result<>(false, message);
            }
        }

        //
        return new Result<>();
    }

    /**
     * 查询缓存的SN记录
     *
     * @param dtlId
     * @param gdnNo
     * @return
     */
    public List<GoodsDto> getSnCacheList(Long dtlId, String gdnNo) {

        String key = gdnNo + dtlId;
        log.info("key:{}", key);
        List<GoodsDto> goodsDtoList = (List<GoodsDto>) RedisUtil.get(key);

//        RedisUtil.set

        return goodsDtoList;
    }

    /**
     * 根据条码删除缓存
     *
     * @param dtlId
     * @param gdnNo
     * @param snList
     * @return
     */
    //TODO 好LOW的代码啊，但是没时间搞哦
    public void deleteSnCacheList(Long dtlId, String gdnNo, List<String> snList) {
        //查询缓存的SN记录
        String key = gdnNo + dtlId;
        List<GoodsDto> goodsDtoList = getSnCacheList(dtlId, gdnNo);
        boolean flag = true;

        if (snList != null) {
            if (goodsDtoList == null ||goodsDtoList.size() == 0) {
                Shift.fatal(StatusCode.OPERATE_ERR, "缓存记录为空");
            }

            if (snList.size() == 1) {
//                if (goodsDtoList != null) {
                List<String> gsnList = goodsDtoList.stream().map(s -> s.getGoodsSn()).collect(Collectors.toList());

                if (!gsnList.containsAll(snList)) {
                    Shift.fatal(StatusCode.OPERATE_ERR, "未在缓存记录中查询到所选条码");
                }
                if (goodsDtoList.size() != 1) {
                    flag = false;
                }
//                }
            } else {
                for (String sn : snList) {
                    if (goodsDtoList.contains(sn)) {
                        flag = true;
                    } else {
                        flag = false;
                        break;
                    }
                }
            }
        }

        if (flag) {
            RedisUtil.delete(key);
        } else {
            //入参snList为null删除该KEY缓存，否则过滤SN，更新缓存
            goodsDtoList = goodsDtoList.stream().filter(s -> !snList.contains(s.getGoodsSn())).collect(Collectors.toList());
            RedisUtil.set(key, goodsDtoList);
        }
    }


    /**
     * 新增/更新缓存
     *
     * @param dtlId
     * @param gdnNo
     * @param goodsDto
     * @param isCommit 是否是提交
     */
    public void insertUpdateCache(Long dtlId, String gdnNo, GoodsDto goodsDto,boolean isCommit) throws SystemException{

        String key = gdnNo + dtlId;
        List<GoodsDto> goodsDtoList = getSnCacheList(dtlId, gdnNo);
        if(isCommit){//提交条码
            if (goodsDtoList == null) {
                Shift.fatal("缓存数据为空，数据可能已经被更改，请重新加载数据");
            }else{
                List<GoodsDto> list = goodsDtoList.stream().filter(item -> item.getGoodsSn().equals(goodsDto.getGoodsSn())).collect(Collectors.toList());
                if(CollectionUtil.isEmpty(list)){
                    throw new SystemException("SN=>:"+goodsDto.getGoodsSn()+"不在缓存里面，数据可能已经被修改，请重新加载数据");
                }
            }

        }else{//校验加入缓存
            //入参snList为null则新增，否则更新
            if (goodsDtoList == null) {
                goodsDtoList = Arrays.asList(goodsDto);
            } else {
                //去重


                List<GoodsDto> list = goodsDtoList.stream().filter(item -> item.getGoodsSn().equals(goodsDto.getGoodsSn())).collect(Collectors.toList());
                if(CollectionUtil.isEmpty(list)){
                    goodsDtoList.add(goodsDto);
                }else{
                    throw new SystemException("SN在缓存里面已经存在"+goodsDto.getGoodsSn());
                }

            }
            RedisUtil.set(key, goodsDtoList);


        }

    }

    /**
     * 需求数量转移
     *
     * @param sn
     * @param dtlId
     * @param servletRequest
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<Integer> countChange(String sn, Integer dtlId, HttpServletRequest servletRequest) {

        String loginName = HttpSessionUtil.getLoginName(servletRequest);
        GdnDtl gdnDtl = gdnDtlMapper.selectById(dtlId);//当前扫描时的明细ID
        Goods goods = goodsMapper.selectBySn(sn);

        Long gdnHeaderId = gdnDtl.getGdnHeaderId();
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);
        //int shipmentedCode = GdnDtlStatusEnum.SHIPMENTED.getCode();//已发运

        String sourceShipNotice = gdnHeader.getSourceShipNotice();//来源发货通知单
        String soNo = goods.getSoNo();
        Integer skuId = goods.getSkuId();
        //销售订单号、物料编码不在【当前】发货单
//        if ((!soNo.equals(gdnDtl.getSkuId()))&&(!skuId.equals(gdnDtl.getSkuId()))) {
//            //但销售订单号、物料编码不在当前发货单，在其他发货单有需求，并且那个发货单明细行状态是已发运(7)。
        GdnDtl gdnDtlChange = gdnDtlMapper.selectBySoSkuIdSourceShipNotice(soNo, skuId, sourceShipNotice, null, sn);
//            if (gdnDtlChange != null) {
//                result.setMessage("SN不是已拣选状态");
//                result.setCode(2);
//                return result;
//            }

        GdnDtl gdnDtlNew = new GdnDtl();
        BeanUtil.copyProperties(gdnDtlChange, gdnDtlNew);
        gdnDtlNew.setModifyTime(null);
        gdnDtlNew.setModifyUser(null);
        gdnDtlNew.setCreateTime(new Date());
        gdnDtlNew.setCreateUser(loginName);
        gdnDtlNew.setGdnDtlId(null);

        gdnDtlNew.setGdnHeaderId(gdnHeaderId);

        gdnDtlNew.setOrderQty(BigDecimal.ZERO);
        gdnDtlNew.setPreAlocQty(BigDecimal.ZERO);
        gdnDtlNew.setPickQty(BigDecimal.ZERO);
        gdnDtlNew.setShipQty(BigDecimal.ZERO);
        gdnDtlNew.setPickQty(BigDecimal.ZERO);
        gdnDtlNew.setRemainQty(BigDecimal.ZERO);


        //设置明细行号
        //创建类型的条件构造器
        QueryWrapper<GdnDtl> wrapper = new QueryWrapper<>();
        wrapper.eq("GDN_HEADER_ID", gdnHeaderId);
        List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(wrapper);
        gdnDtlList.sort((a, b) -> b.getGdnLineNo() - a.getGdnLineNo());


        GdnDtl gdnDtl1 = gdnDtlList.get(0);
        gdnDtlNew.setGdnLineNo(gdnDtl1.getGdnLineNo() + 10);
        gdnDtlNew.setStatus(GdnDtlStatusEnum.WAITLOAD.getCode());
        gdnDtlNew.setOrderQty(BigDecimal.ZERO);
        gdnDtlNew.setWhsId(goods.getWhsId());

        int row = gdnDtlMapper.insert(gdnDtlNew);
        log.info("成功新增发货明细{}条记录", row);

        //
        List<CtnrLoading> ctnrLoadingListSource = ctnrLoadingMapper.selectCtnrListByDtlId(dtlId);
//        List<CtnrLoading> ctnrLoadingListSource = ctnrLoadingMapper.selectCtnrListByDtlId(gdnDtlChange.getGdnDtlId());
        CtnrLoading ctnrLoadingSource = ctnrLoadingListSource.get(0);


        //TODO 复制【装柜单】
//        Integer gdnDtlId = gdnDtlChange.getGdnDtlId();
//        List<CtnrLoading> ctnrLoadingList = ctnrLoadingMapper.selectCtnrListByDtlId(gdnDtlId);
//        CtnrLoading ctnrLoading = ctnrLoadingList.get(0);

        ctnrLoadingSource.setGdnDtlId(gdnDtlNew.getGdnDtlId());
        ctnrLoadingSource.setCreateTime(new Date());
        ctnrLoadingSource.setCreateUser(loginName);
        ctnrLoadingSource.setModifyUser(null);
        ctnrLoadingSource.setModifyTime(null);

        ctnrLoadingSource.setPlanQty(BigDecimal.ZERO);
        ctnrLoadingSource.setCmpQty(BigDecimal.ZERO);

        ctnrLoadingSource.setGdnNo(gdnHeader.getGdnNo());
        ctnrLoadingSource.setGdnLineNo(gdnDtlNew.getGdnLineNo());

        ctnrLoadingSource.setStatus(CtnrLoadingStatusEnum.CREATE.getCode());
//        ctnrLoadingSource.setCntrSeq(ctnrLoadingSource.getCntrSeq());
//        ctnrLoadingSource.setCntrNo(ctnrLoadingSource.getCntrNo());
//        ctnrLoadingSource.setCntrType(ctnrLoadingSource.getCntrType());


        int rowIns = ctnrLoadingMapper.insert(ctnrLoadingSource);
        log.info("成功新增装柜单{}条记录", rowIns);

        //TODO 复制【装柜单】

        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;
        //加缓存
//        GoodsDto goodsDto = new GoodsDto();
//        goodsDto.setGoodsSn(sn);
//        goodsDto.setQty(goods.getQty());
//        insertUpdateCache(dtlId, gdnHeader.getGdnNo(), goodsDto);
        //加缓存

        //添加
        Result result = new Result<Integer>(gdnDtlNew.getGdnDtlId());
        return result;

    }

    /**
     * 结束前校验
     *
     * @param gdnNo
     * @param ctnrNo
     * @param loginName
     * @return
     */
    public Result<?> closeBefore(String gdnNo, String ctnrNo, String loginName) {

        //创建类型的条件构造器
        QueryWrapper<GdnHeader> wrapper = new QueryWrapper<>();
        wrapper.eq("GDN_NO", gdnNo);

        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(wrapper);
        if (!ctnrNo.equals(gdnHeader.getCntrNo())) {
            String msg = String.format("录入柜号[%s]与发货单绑定柜号[%s]不同，请确认后再操作", ctnrNo, gdnHeader.getCntrNo());
            Shift.fatal(StatusCode.BUSINESS_ERR, msg);
        }

        return new Result<>();
    }

    //TODO ZYF:新增OMS回传TMS
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public String tmsToOms(BigDecimal gdnHeaderId,List<BigDecimal> gdnDtlIdList) throws Exception{
        if (cn.hutool.core.collection.CollectionUtil.isEmpty(gdnDtlIdList)){
            Shift.fatal("请勾选发货明细");
        }

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        Integer tmsFlag=new Integer(1);

        //TODO 回传TMS
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>().eq("GDN_HEADER_ID", gdnHeaderId));
        if (tmsFlag.equals(gdnHeader.getTmsFlag())){
            //已经回传过TMS
            Shift.fatal(gdnHeader.getGdnNo()+"已回传TMS!");
        }
        OmsShipBusiness omsShipBusiness = omsShipBusinessMapper.selectOne(new QueryWrapper<OmsShipBusiness>().eq("ORDER_NUMBER", gdnHeader.getSourceShipNotice()).eq("CONSULT_BOX_NUMBER", gdnHeader.getRefCntrNo()));

        if (omsShipBusiness==null){
            Shift.fatal("查无对应的OMS中间信息!");
        }

        String formatDate = dateFormat.format(gdnHeader.getShipDate());
/*        String[] split = formatDate.split(":");
        formatDate=split[0]+":"+split[1];*/
//        System.out.println("=================="+formatDate);
        log.info("格式化的出库时间："+formatDate);
        //设置回传tms主档
        TmsDispatchReturnHeader returnHeader=new TmsDispatchReturnHeader();
        returnHeader.setCustomerOrderId(gdnHeader.getGdnNo());
        returnHeader.setCustomerOrderDate(formatDate);
        returnHeader.setCustomerSendId(omsShipBusiness.getOrderNumber());
        returnHeader.setCustomerSendLineId(omsShipBusiness.getOrderLineNumber());
        returnHeader.setFreightForwarderCode(omsShipBusiness.getForworderCode());
        returnHeader.setFreightForwarderName(omsShipBusiness.getForworderName());
        returnHeader.setShipCompanyCode(omsShipBusiness.getCompanyCode());
        returnHeader.setShipCompanyName(omsShipBusiness.getCompanyName());
        returnHeader.setSoNumber(omsShipBusiness.getSaleNumber());
        returnHeader.setContainerType(omsShipBusiness.getBoxTypeCode());
        returnHeader.setContainerNo(omsShipBusiness.getBoxNumber());
        returnHeader.setCarrierCode(omsShipBusiness.getTransferCode());
        returnHeader.setCarrierName(omsShipBusiness.getTransferName());
        returnHeader.setOperate(TmsOperateEnum.ADD.code);

        //设置回传tms明细
        List<TmsDispatchReturnDtl> dtlList=new ArrayList<>();
        for (BigDecimal gdnDtlId : gdnDtlIdList) {
            GdnDtl gdnDtl = gdnDtlMapper.selectOne(new QueryWrapper<GdnDtl>().eq("GDN_DTL_ID", gdnDtlId));
            if (gdnDtl.getCtnrSeq()==null){
                Shift.fatal("请先配置装柜顺序!");
            }
            Warehouse warehouse = warehouseMapper.selectOne(new QueryWrapper<Warehouse>().eq("WHS_ID", gdnDtl.getWhsId()));
            //CodeMapping codeMapping = codeMappingMapper.selectOne(new QueryWrapper<CodeMapping>().eq("WMS_CODE", warehouse.getWhsCode()).eq("FAC_CODE", gdnHeader.getFactory()).eq("QA_STATUS", gdnDtl.getQaStatus()));
            TmsDispatchReturnDtl dtl=new TmsDispatchReturnDtl();
            dtl.setStorageCode(warehouse.getWhsCode());
            dtl.setStorageName(warehouse.getWhsName());
            dtl.setEstimateLoadBeginTime(formatDate);
            dtl.setEstimateLoadEndTime(formatDate);
            //TODO 待定
            dtl.setRouteOrder(gdnDtl.getCtnrSeq()+"");
            dtlList.add(dtl);
        }
        returnHeader.setLoadAddressAddDtoList(dtlList);
        String param = JSONArray.toJSONString(returnHeader);//最终发送的报文
        log.info(gdnHeader.getGdnNo()+"报文:"+param);

//        Shift.fatal("111111111111");

        //更新回传标识为1
        gdnHeaderMapper.updateTmsFalg(gdnHeaderId);//先改状态再回传TMS，避免出现bug后已经回传TMS

        TmsResponse tmsResponse = tmsHttpClient.sendHttpRequest(header + dispatchReturn, param);
        if (tmsResponse==null){
            Shift.fatal("请求超时!");
        }
        if (!tmsResponse.isSuccess()){
            Shift.fatal("回传TMS失败:"+tmsResponse.getMsg());
        }




        return "回传成功!";
    }

    public void wriiteToTms(String gdnNo) throws Exception{
        IfPostback ifPostback = new IfPostback();
        ifPostback.setDestSystem(OtherSystemEnum.SYSYTEM_TMS.getEnumName());
        ifPostback.setPostbackTopic(TopicEnum.TOPIC_TMS_TAKE_OVER.getEnumName());
        ifPostback.setPostbackResult(BackResultEnum.DEFAULT_STATE.code);
        ifPostback.setCreateTime(new Date());
//        ifPostback.setPostbackTime(new Date());
        ifPostback.setPostbackNo(gdnNo);
        ifPostback.setPostbackSourceNo("");
        ifPostback.setPostbackType(GdnTypeEnum.D01.code);

        String gdnDataWithTms = this.getGdnDataWithTms(gdnNo);
        ifPostback.setPostbackContent(gdnDataWithTms);
        ifPostbackMapper.insert(ifPostback);
    }

    private String getGdnDataWithTms(String gdnNo){
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
        TmsTakeOverDto header=new TmsTakeOverDto();
        List<TmsTakeOverDtl> dtlList = tmsTakeOverMapper.selectTmsGoods(gdnNo);
        header.setGoodsDtoList(dtlList);
        header.setOperate(TmsOperateEnum.ADD.code);
        header.setTotalWeight(new Double(0));
        header.setTotalQuantity(new Double(dtlList.size()));
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>().eq("GDN_NO", gdnNo));
        Date shipDate = gdnHeader.getShipDate();
        if (shipDate==null){
            shipDate=new Date();
        }
        header.setTakeOverTime(dateFormat.format(shipDate));
        header.setTakeOverNo(gdnNo);
        header.setCustomerOrderId(gdnNo);
        return JSONArray.toJSONString(header);
    }

}
